在我看来,如果您知道自己在做什么,即使在派生类的构造函数中也可以安全地使用 new(this)。你只需要确保你的基类有一个虚拟构造函数(它的基类也是如此,一直到整个链条)。例如:
#include <stdio.h>
#include <new>
struct Dummy {};
struct print
{
print(const char *message) { fputs(message, stdout); }
print(const char *format, int arg1) { printf(format, arg1); }
print(const char *format, int arg1, int arg2) { printf(format, arg1, arg2); }
};
struct print2 : public print
{
print2(const char *message) : print(message) {}
print2(const char *format, int arg1) : print(format, arg1) {}
print2(const char *format, int arg1, int arg2) : print(format, arg1, arg2) {}
};
class foo : public print
{
int *n;
public:
foo(Dummy) : print("foo::foo(Dummy) {}\n") {}
foo() : print("foo::foo() : n(new int) {}\n"), n(new int) {}
foo(int n) : print("foo::foo(int n=%d) : n(new int(n)) {}\n", n), n(new int(n)) {}
int Get() const { return *n; }
~foo()
{
printf("foo::~foo() { delete n; }\n");
delete n;
}
};
class bar : public print2, public foo
{
public:
bar(int x, int y) : print2("bar::bar(int x=%d, int y=%d) : foo(x*y) {}\n", x, y), foo(x*y) {}
bar(int n) : print2("bar::bar(int n=%d) : foo(Dummy()) { new(this) bar(n, n); }\n", n), foo(Dummy())
{
__assume(this); // without this, MSVC++ compiles two extra instructions checking if this==NULL and skipping the constructor call if it does
new(this) bar(n, n);
}
~bar()
{
printf("bar::~bar() {}\n");
}
};
void main()
{
printf("bar z(4);\n");
bar z(4);
printf("z.Get() == %d\n", z.Get());
}
输出:
bar z(4);
bar::bar(int n=4) : foo(Dummy()) { new(this) bar(n, n); }
foo::foo(Dummy) {}
bar::bar(int x=4, int y=4) : foo(x*y) {}
foo::foo(int n=16) : n(new int(n)) {}
z.Get() == 16
bar::~bar() {}
foo::~foo() { delete n; }
当然,如果基类具有常量* 或引用成员(或者如果您无法编辑包含基类声明的文件),那么您就不走运了。这样就不可能在其中编写一个虚拟构造函数——更不用说使用“new(this)”了,然后你将初始化这些“常量”成员两次!这就是真正的 C++0x 委托构造函数可以真正派上用场的地方。
请告诉我有关此技术是否还有其他任何不安全或不可移植的地方。
(编辑:我也意识到,也许在一个虚拟类中,虚拟函数表可能会被初始化两次。那将是无害的,但效率低下。我需要尝试一下,看看编译后的代码是什么样的。)
*如果您在基类中只有常量成员(并且没有引用),那么您并非完全不走运。您只需确保所有常量成员的所有类都有自己的虚拟构造函数,基类的虚拟构造函数可以依次调用。但是,如果某些常量具有诸如int之类的内置类型,那么您就不走运了——那些将不可避免地被初始化(例如,一个const int将被初始化为零)。
编辑:这是链接虚拟构造函数的示例,如果int值在 FooBar 类中变为const int 值,则会被破坏:
#include <stdio.h>
#include <new>
struct Dummy {};
struct print
{
print(const char *message) { fputs(message, stdout); }
print(const char *format, int arg1) { printf(format, arg1); }
print(const char *format, int arg1, int arg2) { printf(format, arg1, arg2); }
};
struct print2 : public print
{
print2(const char *message) : print(message) {}
print2(const char *format, int arg1) : print(format, arg1) {}
print2(const char *format, int arg1, int arg2) : print(format, arg1, arg2) {}
};
class FooBar : public print
{
int value;
public:
FooBar() : print("FooBar::FooBar() : value(0x12345678) {}\n"), value(0x12345678) {}
FooBar(Dummy) : print("FooBar::FooBar(Dummy) {}\n") {}
int Get() const { return value; }
};
class foo : public print
{
const FooBar j;
int *n;
public:
foo(Dummy) : print("foo::foo(Dummy) : j(Dummy) {}\n"), j(Dummy()) {}
foo() : print("foo::foo() : n(new int), j() {}\n"), n(new int), j() {}
foo(int n) : print("foo::foo(int n=%d) : n(new int(n)), j() {}\n", n), n(new int(n)), j() {}
int Get() const { return *n; }
int GetJ() const { return j.Get(); }
~foo()
{
printf("foo::~foo() { delete n; }\n");
delete n;
}
};
class bar : public print2, public foo
{
public:
bar(int x, int y) : print2("bar::bar(int x=%d, int y=%d) : foo(x*y) {}\n", x, y), foo(x*y) {}
bar(int n) : print2("bar::bar(int n=%d) : foo(Dummy()) { new(this) bar(n, n); }\n", n), foo(Dummy())
{
printf("GetJ() == 0x%X\n", GetJ());
__assume(this); // without this, MSVC++ compiles two extra instructions checking if this==NULL and skipping the constructor call if it does
new(this) bar(n, n);
}
~bar()
{
printf("bar::~bar() {}\n");
}
};
void main()
{
printf("bar z(4);\n");
bar z(4);
printf("z.Get() == %d\n", z.Get());
printf("z.GetJ() == 0x%X\n", z.GetJ());
}
输出:
bar z(4);
bar::bar(int n=4) : foo(Dummy()) { new(this) bar(n, n); }
foo::foo(Dummy) : j(Dummy) {}
FooBar::FooBar(Dummy) {}
GetJ() == 0xCCCCCCCC
bar::bar(int x=4, int y=4) : foo(x*y) {}
foo::foo(int n=16) : n(new int(n)), j() {}
FooBar::FooBar() : value(0x12345678) {}
z.Get() == 16
z.GetJ() == 0x12345678
bar::~bar() {}
foo::~foo() { delete n; }
(0xCCCCCCCC 是在调试版本中初始化未初始化的内存。)