2015-04-29 9 views
0

Учитывая следующий код:Создать экземпляр базового класса в производной функции члена класса, когда конструктор базового класса защищен

class A 
{ 
protected: 
    A(){}; // compilation error 
public: 
    void foo(){cout << "A\n";}; 
}; 

class B : public A 
{ 
public: 
    B() { }; 
    void foo(){ 
     cout << "B\n"; 
     A A(); // OK 
     A a1; // compilation error 
    } 
}; 

Если я изменил базовый класс A конструктора public, код компилируется. Как это объяснить?

+2

'A a();' объявляет функцию. Конечно, все в порядке. – Columbo

ответ

2

§11.4/1:

Как было описано ранее, доступ к защищенному члену предоставляется потому, что ссылка происходит в другом или членом какого-то класса C. Если доступ должен образовывать указатель на элемент (5.3.1), [...].
Все остальные доступа включают (возможно неявное) выражение объекта (5.2.5). В этом случае класс выражения объекта должен быть C или класс , полученный от C.

В вашем случае доступ подразумевается, но тем не менее присутствует. Мы пытаемся получить доступ к конструктору A с аргументом (неявного) объекта a1. § 11/6:

Все элементы управления доступом в пункте 11 влияют на возможность доступа имя члена класса из объявления конкретного лица [...]. [Примечание: Этот доступ также применяется к неявным ссылкам на конструкторы, функции преобразования и деструкторы. - конец примечание]

Однако a1 не типа B или класса, производного от B. Следовательно, наше вышеописанное требование не выполняется, и контроль доступа не устраивает.

+0

Где упоминается «11/4/1, 11.6 ..»? Спасибо – Christine

+0

@Christine Стандарт языка. – Columbo

2

Для создания объекта конструктор, необходимый для этого, должен быть доступен. В пределах члена B вы можете получить доступ к защищенным членам A, но только через объект типа B. Таким образом, вы не можете получить доступ к защищенному конструктору по умолчанию.

Предыдущая строка A a(); компилируется, потому что объявляет функцию.

+0

Должно быть A A(); Извините, я только что редактировал – Christine

+2

@Christine: Теперь вы объявили функцию с тем же именем, что и класс; поэтому 'A' будет ссылаться на функцию, а' A a1; 'не имеет никакого смысла. Вам нужно будет «class A a1;» для устранения неоднозначности. –

0

Чтобы разрешить доступ к защищенным методам, вы можете использовать friend class. Пример:

class A 
{ 
    friend class B; 
protected: 
    A(){}; 
public: 
    void foo(){cout << "A\n";}; 
}; 

class B : public A 
{ 
public: 
    B() { }; 
    void foo(){ 
     cout << "B\n"; 
     A a1; 
    } 
}; 
1

Если у вас есть какие-либо класс, который имеет защищенную или частную Конструктора, считается, что класс «абстрактный объект класса» Это означает, что она представляет собой концепцию или идею о том, что объект должен быть как и все элементы комментария. Вы не можете создать экземпляр объекта этого типа напрямую! Таким образом, вы должны унаследовать от этого класса, кто конструкторы являются публичными, если они не являются абстрактными. Другая идея состоит в том, чтобы быть специализированным объектом, который не может быть непосредственно создан, но может быть дружественным к другим классам, где их методы могут создавать экземпляр объекта, то есть те классы, у которых есть доступ к другу этого абстрактного класса, смогут создать экземпляр этого объекта, который будет жить в течение жизни и объема объекта класса друзей.Примеры:

class A { 
public: 
    enum ClassType { 
     TYPE_A = 0, 
     TYPE_B, 
     TYPE_C, 
     TYPE_D, 
     TYPE_E, 
     TYPE_F 
    }; 

private: 
    unsigned m_uId; 
protected: 
    explicit A(A::ClassType type) : 
    m_uId(static_cast<unsigned>(type) {} 

    void initialize() = 0; // Purely Abstract - Each Derived Class Must Create This Function 
    void cleanup();  
}; 

class B sealed : public A { 
public: 
    B() : A(TYPE_A) {} 
    void initialize() override; 
}; 

class C sealed : public A { 
public: 
    C() : A(TYPE_C) {} 
    void initialize() override; 
}; 

class D : public A { 
protected: 
    D(A::ClassType type) : A(type) {}   
    void initialize() override = 0; 
}; 

class E sealed : public D { 
public: 
    E() : D(TYPE_E) {} 
    void initialize() override; 
}; 

class F sealed : public D { 
public: 
    F : D(TYPE_F) {} 
    void initialize() override; 
}; 

Здесь я демонстрирую полиморфное наследование. Класс A & D вы не можете создавать объекты для объектов, однако классы B, C, E & F вы можете создавать объекты. Поскольку конструкторы для A & D защищены, любой производный класс имеет к ним доступ. С помощью этой установки каждый класс имеет доступ к A :: cleanup(), и каждый класс должен реализовать свою функцию: initialize() override.

Для этой следующей части я продемонстрирую использование абстрактного класса, который могут использовать классы друзей.

class Counter { 
    friend unsigned Object1::getCount(); 
    friend unsigned Object2::getCount(); 
private: 
    static unsigned m_uCounter;  
    Counter() { ++m_uCounter; } 

public: 
    unsigned getCounter() { return m_uCounter; }  
}; 

class Object1 { 
    friend class Counter; 
private: 
    unsigned m_uCount; 
public: 
    Object1() : m_uCount(0) {} 
    void count() { 
     Counter counter; 
     m_uCount = counter.getCounter(); 
    } 

    unsigned getCount() const { return m_uCounter; }  
}; 

class Object2 { 
    friend class Counter; 
private: 
    unsigned m_uCount; 
public: 
    Object2() : m_uCount(0) {} 
    void count() { 
     Counter counter; 
     m_uCount = counter.getCounter(); 
    } 

    unsigned getCount() const { return m_uCount; } 
}; 

Этого код показывает, как использовать абстрактный класс, который не может быть объявлен сама по себе, но может быть использована в других классах, будучи другим классом. В этой ситуации только демонстрацией единственной целью класса Counter является выполнение работы по увеличению. Класс счетчика не может быть автономным объектом, который не имеет смысла иметь, однако его использование в других классах через объявления друзей позволяет этим классам иметь доступ к его конструктору через объявление функции из внешних классов, объявленных как функции друга в абстрактном классе. Такая настройка позволяет только Object1 :: getCount() и Object2 :: getCount() объявлять тип счетчика и иметь доступ к Counter :: getCounter().

Надеюсь, это поможет вам понять в абстрактных классах, которые касаются полиморфизма с использованием наследования, ассоциации абстрактного класса через использование друга и как правильно объявить абстрактный тип.

В вашем коде, в котором другие адресовали A A(); объявляет прототип функции, где A a1; пытается объявить тип A, который является абстрактным!

+0

В первом разделе примеров я использовал мир, запечатанный в некоторых определениях классов, что это слово делает, он сообщает компилятору, что вы не ожидаете получить от него какие-либо производные типы. Как вы можете заметить, иерархия A не запечатана, таким образом, B, C & D вытекают из нее. Оба B & C запечатаны, но D не запечатаны, а E & F получены из D, которые оба запечатаны. –

+0

Я не знаю, доступно ли вам закрытое ключевое слово или нет, но я использую VS2013. –