Почему защищенный конструктор вызывает ошибку в этом коде?
Один вопрос об защищенном конструкторе. Я узнал, что защищенный конструктор может использоваться в производном классе. Как бы то ни было, я обнаружил, что приведенный ниже код имеет ошибку. Почему так происходит?
class A
{
protected:
A(){}
};
class B: public A {
public:
B() {
A* f=new A(); // Why it is not working here
}
};
Ответы
Ответ 1
Это не имеет никакого отношения к конструкторам. Вот как работает protected
доступ.
Как работает спецификатор доступа protected
, он позволяет производному классу B
получить доступ к содержимому объекта базового класса A
только тогда, когда этот объект класса A
является подобъектом класса B
, Это означает, что единственное, что вы можете сделать в своем коде, - это получить доступ к содержимому A
через B
: вы можете получить доступ к элементам A
через указатель типа B *
(или ссылку типа B &
). Но вы не можете получить доступ к тем же элементам с помощью указателя типа A *
(или ссылки A &
).
Рассмотрим следующий пример
class A {
protected:
int i;
};
class B : A {
void foo() {
i = 0; // OK
this->i = 0; // OK
B *pb = this;
pb->i = 0; // OK
A *pa = this;
pa->i = 0; // ERROR
((A *) this)->i = 0; // ERROR
}
};
В приведенном выше B::foo
вы можете получить доступ к базовому элементу A::i
, используя простой синтаксис i
. Это эквивалентно использованию синтаксиса this->i
. Оба будут работать, потому что указатель this
имеет тип B *
, т.е. Вы обращаетесь к A::i
тщательно указателю типа B *
. Это именно то, что должно разрешить спецификатор доступа protected
. Доступ с помощью указателя pb
работает по той же причине.
Однако, когда вы "конвертируете" this
указатель на тип A *
, вы больше не можете обращаться к A::i
с помощью этого нового указателя, даже если вы все еще пытаетесь получить к нему доступ тот же самый член, что и раньше.
При применении к конструкторам спецификатор доступа protected
имеет очень специфический эффект: защищенный конструктор может использоваться только для инициализации подобъектов базового класса. Он не может использоваться для инициализации автономных объектов (это то, что вы пытались сделать). Другими словами, защищенные конструкторы - это еще один способ реализовать концепцию абстрактного класса в С++ (наряду с чистыми виртуальными методами). Если конструкторы вашего класса защищены, то ваш класс является абстрактным. Вы не можете использовать его для определения независимых объектов "извне". (Конечно, вышесказанное не применяется в друзьях, а также внутри самого класса).
Ответ 2
Если базовый класс имеет защищенный конструктор, вы не можете создавать экземпляр класса напрямую. Но вы можете сделать это, чтобы вызвать конструктор из конструктора базового класса:
class A {
protected:
A() {}
};
class B: public A {
public:
B() : A() // allowed to access constructor like this
{
A* f = new A(); // Not allowed to access constructor like this!
}
};
Прямой вызов конструктора, как показано ниже, дает следующую ошибку с gcc версии 4.1.2:
A* f = new A(); // Not allowed to access constructor like this!
test.cpp:4: error: A::A() is protected
Однако этот вызов конструктору не вызывает ошибок:
B() : A() // allowed to access constructor like this
Причина этого в том, что второй вызов обращается к конструктору A() через наследование, что разрешено. Однако он пытается явно создать новый экземпляр A(), вызвав конструктор напрямую:
A* f = new A(); // Not allowed to access constructor like this!
Это может показаться неинтуитивным, так как B должен иметь доступ к конструктору, потому что B наследует от A. Однако, если вы объявите конструктор protected в С++, вы не сможете создать экземпляр этого класса, кроме как через наследование или дружеские отношения.
Ответ 3
Позвольте мне ответить следующим образом:
1) Конструкторы не получают Inherited, и поэтому в производном классе они не могут быть перегружены.
2) Конструкторы вызываются и не вызываются.
3) Если вы объявили простую функцию в say protected void print(), а затем попытались позвонить в B, это сработало бы. Это происходит bcoz, B унаследовал эту функцию.
4) Когда вы делаете что-то вроде этого b: a(), вы вызываете конструктор и разрешаете.
5) Попробуйте сделать B классом друзей класса A, а затем запустите и посмотрите, работает ли он.
Надеюсь, что это поможет.
Ответ 4
У меня был такой же вопрос, как и этот, и эта ссылка дает мне понять.
cppreference говорит вот так:
Protected members form the interface for the derived classes (which is
distinct from the public interface of the class).
A protected member of a class Base can only be accessed
1) by the members and friends of Base
2) by the members and friends (until C++17) of any class derived from Base, but only when operating on an object of a type that is derived from Base (including this)