Tôi đang bối rối về trạng thái của một đối tượng sau nó được di chuyển bằng cách sử dụng ngữ nghĩa di chuyển C++ 0x. Sự hiểu biết của tôi là khi một đối tượng đã được di chuyển, nó vẫn là một đối tượng hợp lệ, nhưng trạng thái bên trong của nó đã bị thay đổi để khi destructor của nó được gọi, không có tài nguyên nào được deallocated.Vật thể zombie sau std :: di chuyển
Nhưng nếu hiểu biết của tôi là chính xác, hàm hủy của đối tượng đã di chuyển nên vẫn được gọi.
Nhưng, điều đó không xảy ra khi tôi thực hiện một thử nghiệm đơn giản:
struct Foo
{
Foo()
{
s = new char[100];
cout << "Constructor called!" << endl;
}
Foo(Foo&& f)
{
s = f.s;
f.s = 0;
}
~Foo()
{
cout << "Destructor called!" << endl;
delete[] s; // okay if s is NULL
}
void dosomething() { cout << "Doing something..." << endl; }
char* s;
};
void work(Foo&& f2)
{
f2.dosomething();
}
int main()
{
Foo f1;
work(std::move(f1));
}
sản lượng này:
Constructor called!
Doing something...
Destructor called!
Thông báo destructor chỉ được gọi một lần. Điều này cho thấy rằng sự hiểu biết của tôi ở đây là tắt. Tại sao không phải là destructor được gọi là hai lần? Dưới đây là giải thích của tôi về những gì nên đã xảy ra:
Foo f1
được xây dựng.Foo f1
được chuyển đếnwork
, trong đó có giá trị làf2
.- Hàm khởi động di chuyển của
Foo
là được gọi, di chuyển tất cả các tài nguyên trongf1
đếnf2
. - Hiện tại, của trình phá hủy được gọi là, phát hành tất cả các tài nguyên.
- Bây giờ
f1
của destructor được gọi là, mà không thực sự làm bất cứ điều gì vì tất cả các nguồn lực đã được chuyển giao đếnf2
. Tuy nhiên, destructor là gọi là dù sao.
Nhưng vì chỉ có một destructor được gọi là, bước 4 hoặc bước 5 không xảy ra. Tôi đã làm một backtrace từ destructor để xem nơi nó đã được invoked từ, và nó đang được gọi từ bước 5. Vậy tại sao không phải là f2
's destructor cũng được gọi là?
EDIT: OK, tôi đã sửa đổi điều này để nó thực sự quản lý tài nguyên. (Một bộ nhớ đệm nội bộ.) Tuy nhiên, tôi nhận được cùng một hành vi mà destructor chỉ được gọi một lần.
trình biên dịch nào bạn đang thử nghiệm? – jalf
gcc 4.3 ......................... – Channel72
Trong trường hợp đó, có thể đáng lưu ý rằng nó được viết dựa trên phiên bản cũ hơn nhiều của C++ 0x dự thảo. Và di chuyển ngữ nghĩa đã được thay đổi khá nhiều kể từ đó. Ví dụ, tôi tin rằng một trình biên dịch mới hơn sẽ từ chối mã của bạn hoàn toàn trước khi bạn thêm 'std :: move'. Nó sẽ không thể gọi 'công việc' bởi vì đối số là một tham chiếu lvalue hiệu quả bởi vì nó được đặt tên. – jalf