C ++通过继承在受保护的构造函数的基类堆上分配对象

时间:2010-12-25 18:49:49

标签: c++ inheritance

我有一个带有受保护构造函数的类:

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

现在我从它派生并定义了两个静态函数,我设法实际创建了类B的对象,但不是在堆上:

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!

问题是:1)VS2010在允许第一种情况下是错误的吗? 2)是否可以在不修改B的情况下创建B的对象(没有友谊,也没有额外的功能)。 我问,因为在处理B及其成员函数的实例时可以做出类似的事情,请参阅: http://accu.org/index.php/journals/296

提前感谢您的任何建议!

亲切的问候

2 个答案:

答案 0 :(得分:5)

  1. 是的,此代码不符合规定。这与受保护成员访问的特殊规则有关(C ++ 03 draft,11.5 / 1):

      

    当派生类的朋友或成员函数引用受保护的非静态成员函数或   受保护的基类的非静态数据成员,除了前面第11.10节中描述的那些之外,还应用访问检查。除非形成指向成员的指针(5.3.1),否则访问必须通过指向,引用或派生类本身的对象(或从该类派生的任何类)(5.2.5)。

    当你使用B()或new B()时,你通过指向基类的指针有效地使用构造函数。

  2. 您可以创建A类型的对象(我假设A是已发布的 - 没有其他成员/非静态函数)而是使用它。如果你在堆栈上创建它,一切都应该正常工作,除非你试图为它分配其他类型的B对象。如果你在堆上创建它,只要B的析构函数是虚拟的,一切都很好。如果B的析构函数不是虚拟的,并且你将新的A()作为B *返回,那么删除指针在技术上是未定义的行为(5.3.5 / 3:

      

    在第一个备选(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数的动态类型的基类,静态类型应具有虚拟析构函数或者行为未定义。

    但是你可能会发现它在实践中运作良好,所以如果没有其他解决方法(例如将它作为最后手段使用),你可以依赖实际行为。

答案 1 :(得分:2)

protected实际意味着什么存在共同的误解。这意味着派生类可以访问本身上的特定成员而不是其他对象。编译器应该拒绝这两个函数,因为它正在访问不是派生类型的对象的构造函数。

另一个例子,更容易讨论它的正确性:

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

注释行是一个错误,因为它试图修改base类型的对象,而不一定是derived对象。如果语言允许编译代码,那么您将能够修改base类型的对象,甚至类型derived1derived2的对象...有效地破坏访问规则。 / p>

struct derived2 : base {};
int main() {
   base b;
   derived2 d;
   derived::modify( b );    // modifying a base!!!
   derived::modify( d );    // modifying a derived2!!!
}