我有一个带有受保护构造函数的类:
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
提前感谢您的任何建议!
亲切的问候
答案 0 :(得分:5)
是的,此代码不符合规定。这与受保护成员访问的特殊规则有关(C ++ 03 draft,11.5 / 1):
当派生类的朋友或成员函数引用受保护的非静态成员函数或 受保护的基类的非静态数据成员,除了前面第11.10节中描述的那些之外,还应用访问检查。除非形成指向成员的指针(5.3.1),否则访问必须通过指向,引用或派生类本身的对象(或从该类派生的任何类)(5.2.5)。
当你使用B()或new B()时,你通过指向基类的指针有效地使用构造函数。
您可以创建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
类型的对象,甚至类型derived1
,derived2
的对象...有效地破坏访问规则。 / p>
struct derived2 : base {};
int main() {
base b;
derived2 d;
derived::modify( b ); // modifying a base!!!
derived::modify( d ); // modifying a derived2!!!
}