views:

204

answers:

3

One question about protected constructor. I learnt that the protected constructor can be used in the derived class. How ever, I found the code below has an error. Why does it happen like this?

class A
{
    protected:
        A(){}
};

class B: public A {
    public:
        B() {
            A* f=new A();           // Why it is not working here
        }
};
+5  A: 

When a base class has a protected constructor, you can't instantiate the class directly. But you can do this to call the constructor from the base class constructor:

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!
   }
};

A direct call to the constructor as shown below gives you the following error with gcc version 4.1.2:

      A* f = new A(); // Not allowed to access constructor like this!

test.cpp:4: error: A::A() is protected

However, you this call to the constructor gives no errors:

   B() : A() // allowed to access constructor like this

The reason behind this is that the second call accesses the A() constructor through inheritance, which is allowed. However, this tries to explicitly create a new instance of A() by calling the constructor directly:

      A* f = new A(); // Not allowed to access constructor like this!

This might seem unintuitive, as B should be able to access A's constructor because B inherits from A. However, if you declare a constructor protected in C++, you can't create an instance of that class except through inheritance or a friend relationship.

James Thompson
Can you explain why?
skydoor
@James - while you are generally correct, there are two ways you can create an instance of a class with a protected constructor. 1) Within a member function of the same class. 2) Within a `friend`
R Samuel Klatchko
@R Samuel Klatchko - good point, I added a note about friends.
James Thompson
+7  A: 

This has nothing to do with constructors specifically. This is just how protected access works.

The way protected access specifier works, it allows the derived class B to access the contents of an object of base class A only when that object of class A is a subobject of class B. That means that the only thing you can do in your code is to access the contents of A through B: you can access the members of A through a pointer of type B * (or a reference of type B &). But you cannot access the same members it through a pointer of type A * (or reference A &).

Consider the following example

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
  }
};

In the above B::foo, you can access base member A::i by using just plain i syntax. This is equivalent to using this->i syntax. Both will work, because the pointer this has type B *, i.e. you are accessing A::i thorough a pointer of type B *. This is exactly what the protected access specifier is supposed to allow. The access through pb pointer works for the very same reason.

However, when you "convert" this pointer to type A *, you can no longer access A::i through that new pointer, even though you are still trying to access they very same member as before.

When applied to constructors, the protected access specifier has a very specific effect: a protected constructor can only be used to initialize base-class subobjects. It cannot be used to initialize standalone objects (which is what you were trying to do). In other words, protected constructors are another way to implement the concept of abstract class in C++ (along with pure virtual methods). If the constructors of your class are protected, then your class is effectively abstract. You can't use it to define independent objects "from outside". (Of course, the above does not apply within friends, as well as within the class itself).

AndreyT
+1 of course. But notice there is a way to access a protected nonstatic member of `A` from the scope of `B` by using member pointers: http://stackoverflow.com/questions/75538/hidden-features-of-c/1065606#1065606 . It's dirty, but 100% Standard :)
Johannes Schaub - litb
I have doubts about the validity of this behavior though, because i find no accurate wording of how access is done for `A`'s constructor. In fact, i rather find indications that it *should* be allowed - `12/2` says "[Example: declaring a constructorprotected ensures that only derived classes and friends can create objects using it. ]". It's of course still possible that this only applies to subobjects and not complete objects of type `A`, but i would be glad if there was a strict (and normative) rule about it.
Johannes Schaub - litb
In fact, the text of C++0x about the restriction of protected member access for nonstatic function or data members have been changed (`11.5` in n3035). It now apparently assumes the existence of a class-member-access expression for accesses that don't use a qualified-id. Constructors are called implicitly and there is no class-member access expression. It seems to me that either there is a defect, or the Standard intends for `new A` in the scope of `B` being well-formed (although it strikes me as being very ugly). (in fact, the text of c++03 didn't even allow `new A` in the scope of `A` :)).
Johannes Schaub - litb
I've just started a question on `comp.std.c++` about various questions i have about constructor/destructor access checking. The post should appear as soon as mods accept it. ( http://codepad.org/oGQbQeT5 )
Johannes Schaub - litb
A: 

Let me put my answer in steps:

1) Constructors don't get Inherited and that why in derived class, they can't be over ridden.
2) Constructors are invoked and not called.
3) If you have declared a simple function in A say protected void print() and then tried calling it in B, It would have worked. This happens bcoz, B has inherited this function.

4) When you do something like this b : a(), you are invoking the constructor and that's allowed.
5) Try making B a friend class of A and then run and see if it works.

Hope this helps.

Duleb