Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ allocate objects on heap of base class with protected constructors via inheritance

I have a class with protected constructor:

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

Now I derive from it and define two static functions and I manage to actually create objects of the class B, but not on the heap:

class A : public B {
public:
    static B createOnStack() {return B();}
    //static B* createOnHeap() {return new B;} //Compile time Error on VS2010
};

B b = A::createOnStack(); //This works on VS2010!

The question is: 1) Is VS2010 wrong in allowing the first case? 2) Is it possible to create objects of B without modifying B in any way (no friendship and no extra functions). I am asking, because it is possible to make something similar when dealing with instances of B and its member functions, see: http://accu.org/index.php/journals/296

Thank you in advance for any suggestion!

Kind regards

like image 751
StephQ Avatar asked Dec 25 '10 18:12

StephQ


2 Answers

  1. Yes, this code is non-compliant. This is related to special rules for protected member access (C++03 draft, 11.5/1):

    When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11.10). Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5).

    When you use B() or new B(), you're effectively using the constructor through a pointer to the base class.

  2. You can create an object of type A (I assume that A is as posted - no additional members/non-static functions) and use it instead. If you're creating it on stack, everything should work fine, unless you're trying to assign other objects of type B to it. If you're creating it on heap, everything is fine as long as B's destructor is virtual. If B's destructor is not virtual, and you're returning new A() as a B*, then deleting the pointer is technically undefined behavior (5.3.5/3:

    In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.

    However you'll probably find it working fine in practice, so you can rely on the actual behavior if there is no other workaround (i.e. use it as a last resort).

like image 186
zeuxcg Avatar answered Oct 04 '22 08:10

zeuxcg


There is a common misunderstanding on what protected actually means. It means that the derived class can access that particular member on itself not on other objects. The compiler should have rejected both functions as in both cases it is accessing the constructor of an object that is not of the derived type.

Another example, easier to discuss for its correctness would be:

struct base {
protected:
   int x;
};
struct derived : base{
   static void modify( base& b ) {
      b.x = 5; // error
   }
};

The commented line is an error as it is trying to modify an object of type base, not necessarily a derived object. If the language allowed that code to compile, then you would be able to modify an object of type base or even objects of types derived1, derived2... effectively breaking access rules.

struct derived2 : base {};
int main() {
   base b;
   derived2 d;
   derived::modify( b );    // modifying a base!!!
   derived::modify( d );    // modifying a derived2!!!
}
like image 37
David Rodríguez - dribeas Avatar answered Oct 04 '22 09:10

David Rodríguez - dribeas