C++ 控制派生类中的基构造函数,可能是双重初始化
关于这个问题,似乎有一个例外,但它给我提出的问题比回答问题更多。考虑这一点:C++ 控制派生类中的基构造函数,可能是双重初始化,c++,c++11,constructor,virtual-functions,C++,C++11,Constructor,Virtual Functions,关于这个问题,似乎有一个例外,但它给我提出的问题比回答问题更多。考虑这一点: #include <iostream> using namespace std; struct base { virtual void test() {cout << "base::test" << endl;} base() {test();} virtual ~base() {} }; struct derived : base { virtual void te
#include <iostream>
using namespace std;
struct base {
virtual void test() {cout << "base::test" << endl;}
base() {test();}
virtual ~base() {}
};
struct derived : base {
virtual void test() {cout << "derived::test" << endl;}
derived() : base() {}
~derived() {}
};
int main() {
derived d;
return 0;
}
。。。我希望以同样的方式调用基本构造函数两次。然而,编译器似乎将其视为与此相同的情况,这是有原因的吗
derived::derived() : base() { }
我不确定。但就观察到的效应而言,这些似乎是等价的陈述。它与我所想到的基本构造函数可以被转发(至少在某种意义上)的想法背道而驰,或者使用:base()
语法在派生类中选择更好的词。事实上,这种表示法要求基类放在派生类不同的成员之前
换句话说(暂时忘记它的C#)会调用基构造函数两次吗?虽然我理解它为什么会这样做,但我不理解它为什么不会表现得更“直观”,并选择基本构造函数(至少对于简单的情况)并只调用一次
这不是双重初始化对象的风险吗?还是在编写构造函数代码时,假设对象未初始化的部分?最糟糕的情况是,我现在是否必须假设每个类成员都可能被初始化两次,并对此加以防范
我将以一个可怕的例子结束——但这不会泄漏内存吗?它应该会泄漏吗
#include <iostream>
using namespace std;
struct base2 {
int * member;
base2() : member(new int) {}
base2(int*m) : member(m) {}
~base2() {if (member) delete member;}
};
struct derived2 : base2 {
derived2() : base2(new int) {
// is `member` leaking?
// should it be with this syntax?
}
};
int main() {
derived2 d;
return 0;
}
#包括
使用名称空间std;
结构base2{
国际*成员;
base2():成员(新int){}
base2(int*m):成员(m){}
~base2(){if(member)delete member;}
};
结构派生2:base2{
derived2():base2(新int){
//“成员”是否泄漏?
//应该使用这种语法吗?
}
};
int main(){
衍生的2d;
返回0;
}
要构造派生类对象,编译器需要构造其基本部分。您可以通过derived2():base2(newint)
指定基类构造函数编译器应该使用的
若您缺少这样的规范,编译器将使用基本默认构造函数
因此,基构造函数只会被调用一次,只要代码没有导致内存泄漏,就不会有任何泄漏。要构造派生类对象,编译器需要构造它的基部分。您可以通过
derived2():base2(newint)
指定基类构造函数编译器应该使用的
若您缺少这样的规范,编译器将使用基本默认构造函数
所以,基构造函数将只被调用一次,并且只要您的代码没有导致内存泄漏,就不会有任何泄漏
但这不会泄漏内存吗?它应该会泄漏吗
#include <iostream>
using namespace std;
struct base2 {
int * member;
base2() : member(new int) {}
base2(int*m) : member(m) {}
~base2() {if (member) delete member;}
};
struct derived2 : base2 {
derived2() : base2(new int) {
// is `member` leaking?
// should it be with this syntax?
}
};
int main() {
derived2 d;
return 0;
}
否。操作顺序为:
derived2::derived2()
auto p = new int
base2::base2(p)
base2::member = p
对于析构函数:
derived2::~derived2() (implied)
base2::~base2()
if (base2::member) { delete base2::member; }
一个新的,一个删除。太好了
不要忘记编写正确的赋值/复制构造函数
但这不会泄漏内存吗?它应该会泄漏吗
#include <iostream>
using namespace std;
struct base2 {
int * member;
base2() : member(new int) {}
base2(int*m) : member(m) {}
~base2() {if (member) delete member;}
};
struct derived2 : base2 {
derived2() : base2(new int) {
// is `member` leaking?
// should it be with this syntax?
}
};
int main() {
derived2 d;
return 0;
}
否。操作顺序为:
derived2::derived2()
auto p = new int
base2::base2(p)
base2::member = p
对于析构函数:
derived2::~derived2() (implied)
base2::~base2()
if (base2::member) { delete base2::member; }
一个新的,一个删除。太好了
不要忘记编写正确的赋值/复制构造函数。第一个示例是使用“隐式int”返回类型的无效代码。函数
test
需要指定的返回类型。请修复并重新检查。编辑的程序不显示您描述的行为。请参见使用void
返回类型编译代码,但我无法重现隐含的编译器错误。无论如何,对于编译器错误,编译器和版本以及完整调用(逐字)是相关的。注意,谢谢。我知道这并没有表现出问题,我需要向它添加更多的工作代码(我不认为涉及到它)现在必须结束,我的示例中充斥着宏。TL;DRbase
也继承自一个类,该类定义了用于跟踪目的的每个构造函数和运算符(非平凡构造函数)。基本上,我需要重新解释整个问题,示例不适合,对不起…第一个示例是无效代码,使用“implicit int”返回类型。函数test
需要指定的返回类型。请修复并重新检查。编辑的程序不显示您描述的行为。请参见使用void
返回类型编译代码,但我无法重现隐含的编译器错误。无论如何,对于编译器错误,编译器和版本以及完整调用(逐字)是相关的。注意,谢谢。我知道这并没有表现出问题,我需要向它添加更多的工作代码(我不认为涉及到它)现在必须结束,我的示例中充斥着宏。TL;DRbase
也继承自一个类,该类定义了用于跟踪目的的每个构造函数和运算符(非平凡构造函数)。基本上,我需要重新解释整个问题,这个例子并不适合,对不起…谢谢,这是我认为应该发生的事情,但我设法打破了这一点。当我弄明白那是什么时,如果我有什么想法的话,我会再问一次。关于derived::derived(){base::base();}
。。。MSV确实调用了base()
两次。这看起来像是shortgun…Clang的工作原理与MSV相同,gcc无法编译,并显示消息“无法直接调用构造函数'D2::B2[-fppermissive]”。谢谢,这是我认为应该发生的事情,但我设法打破了这一点。当我弄明白那是什么时,如果我有什么想法的话,我会再问一次。关于derived::derived(){base::base();}
。。。MSV确实调用了base()
两次。这看起来像是shortgun…Clang的工作原理与MSV相同,gcc无法编译,并显示消息“无法直接调用构造函数'D2::B2'[-fppermissive]”