在我看來,即使在派生類的構造函數中也可以安全地使用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),那麼您不幸運 - 這些將不可避免地被初始化(例如,常量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());
}
輸出:這裏是鏈接虛擬構造的一個例子,如果int值成爲類FooBar的內部const int的值將被打破
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是什麼未初始化的內存在調試版本initalized。)
這是h我通常做我的代碼。使用我上面描述的方法對我來說似乎很危險,但我不想告訴學生「不這樣做,因爲我不喜歡它的外觀」。但是,在實踐中,這是我通常處理這種情況的方式,也是我向學生們提出的第一個建議。 – 2010-03-22 20:40:14