Tôi tin rằng câu trả lời đầu là sai và vẫn sẽ bị rò rỉ bộ nhớ. Hàm hủy cho các thành viên lớp sẽ không được gọi nếu hàm tạo đưa ra một ngoại lệ (vì nó không bao giờ hoàn thành khởi tạo của nó và có lẽ một số thành viên chưa bao giờ đạt được các hàm tạo của chúng). destructors của họ chỉ được gọi trong cuộc gọi destructor của lớp. Điều đó chỉ có ý nghĩa.
Chương trình đơn giản này minh họa nó.
#include <stdio.h>
class A
{
int x;
public:
A(int x) : x(x) { printf("A constructor [%d]\n", x); }
~A() { printf("A destructor [%d]\n", x); }
};
class B
{
A a1;
A a2;
public:
B()
: a1(3),
a2(5)
{
printf("B constructor\n");
throw "failed";
}
~B() { printf("B destructor\n"); }
};
int main()
{
B b;
return 0;
}
Với kết quả như sau (sử dụng g ++ 4.5.2):
A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted
Nếu constructor của bạn bị lỗi partway thì đó là trách nhiệm của bạn để đối phó với nó. Tệ hơn nữa, ngoại lệ có thể bị ném ra khỏi hàm tạo của lớp cơ sở của bạn! Cách để đối phó với những trường hợp này là sử dụng một "chức năng thử khối" (nhưng ngay cả sau đó bạn phải cẩn thận mã sự hủy diệt của đối tượng khởi tạo một phần của bạn).
Cách tiếp cận chính xác cho vấn đề của bạn sau đó sẽ là một cái gì đó như thế này:
#include <stdio.h>
class A
{
int x;
public:
A(int x) : x(x) { printf("A constructor [%d]\n", x); }
~A() { printf("A destructor [%d]\n", x); }
};
class B
{
A * a1;
A * a2;
public:
B()
try // <--- Notice this change
: a1(NULL),
a2(NULL)
{
printf("B constructor\n");
a1 = new A(3);
throw "fail";
a2 = new A(5);
}
catch (...) { // <--- Notice this change
printf("B Cleanup\n");
delete a2; // It's ok if it's NULL.
delete a1; // It's ok if it's NULL.
}
~B() { printf("B destructor\n"); }
};
int main()
{
B b;
return 0;
}
Nếu bạn chạy nó, bạn sẽ nhận được kết quả mong muốn mà chỉ có các đối tượng được phân bổ đều bị phá hủy và giải phóng.
B constructor
A constructor [3]
B Cleanup
A destructor [3]
terminate called after throwing an instance of 'char const*'
Aborted
Bạn vẫn có thể làm việc với con trỏ được chia sẻ thông minh nếu muốn, với bản sao bổ sung. Viết một hàm tạo tương tự như sau:
class C
{
std::shared_ptr<someclass> a1;
std::shared_ptr<someclass> a2;
public:
C()
{
std::shared_ptr<someclass> new_a1(new someclass());
std::shared_ptr<someclass> new_a2(new someclass());
// You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack.
a1 = new_a1;
a2 = new_a2;
}
}
Chúc may mắn, Tzvi.
Tôi biết, tôi có một bản chất khủng khiếp, rằng tôi không thể cưỡng lại nitpick. Tôi không thể giúp được. 2 xu của tôi: tuyên bố objsomeclass = someclass(); là không cần thiết. Trong phần thân của hàm tạo, objsomeclass đã được khởi tạo mặc định. objsomeclass (someclass()) dưới đây không có ý nghĩa. –
Tôi đồng ý, nhưng nghĩ rằng someclass có một constructor rõ ràng.Và tôi muốn tập trung vào đối tượng được tạo ra trong constructor – yesraaj
Vâng, tôi biết nó chỉ là một ví dụ. Đó là lý do tại sao tôi gọi nó là nitpicking. Cơ sở xây dựng BTW() có thể được công khai :) –