2012-05-14 42 views
5

Tôi có câu hỏi liên quan đến vị trí cú pháp new trong C++. Hai đoạn mã sau đây có tương đương về mặt chức năng và có thể được sử dụng thay thế cho nhau không (tôi không ngụ ý rằng đoạn mã thứ hai nên được sử dụng, khi đoạn mã thứ nhất phù hợp)?Vị trí hành vi mới tương đương

# 1

T* myObj = new T(); 
// Do something with myObj 
delete myObj; 

# 2

char* mem = new char[sizeof(T)]; 
T* myObj = new (mem) T(); 
// Do something with myObj 
myObj->~T(); 
delete[] mem; 

Có cái gì tôi nên đặc biệt cẩn thận trong khi tôi đang sử dụng vị trí cú pháp mới như thế này?

Trả lời

11

Chúng không tương đương, vì chúng có hành vi khác nhau nếu hàm tạo hoặc hàm hủy của số T ném.

new T() sẽ giải phóng bất kỳ bộ nhớ nào đã được cấp phát trước khi cho phép ngoại lệ tuyên truyền thêm nữa. char* mem = new char[sizeof(T)]; T* myObj = new (mem) T(); sẽ không (và trừ khi bạn làm điều gì đó một cách rõ ràng để đảm bảo rằng nó được giải thoát, bạn sẽ bị rò rỉ). Tương tự, delete myObj sẽ luôn phân phối bộ nhớ, bất kể số lượng ~T() có ném hay không.

Một tương đương chính xác cho T* myObj = new T();/*other code*/delete myObj; sẽ là một cái gì đó như:

//When using new/delete, T::operator new/delete 
//will be used if it exists. 
//I don't know how do emulate this in 
//a generic way, so this code just uses 
//the global versions of operator new and delete. 
void *mem = ::operator new(sizeof(T)); 
T* myObj; 
try { 
    myObj = new (mem) T(); 
} 
catch(...) { 
    ::operator delete(mem); 
    throw; 
} 
/*other code*/ 
try { 
    myObj->~T(); 
    ::operator delete(mem); 
} 
catch(...) { 
    //yes there are a lot of duplicate ::operator deletes 
    //This is what I get for not using RAII): 
    ::operator delete(mem); 
    throw; 
} 
+0

Bộ nhớ được cấp phát bởi 'new char [sizeof (T)]' có được căn chỉnh đúng không? –

+3

@TadeuszKopec: Tiêu chuẩn đảm bảo rằng bộ nhớ có liên kết tối đa có thể. Điều này chỉ giữ nếu bạn sử dụng các sắp xếp tự nhiên (tức là xuất hiện với các kiểu dựng sẵn), nếu bạn sử dụng các pragmas để làm tăng sự liên kết của một loại, cao hơn gấp hai lần, thì tất cả các phiên cược sẽ bị tắt. –

8

Vì bạn đang phân bổ bộ nhớ thô, tương đương gần sẽ là:

void *mem = operator new(sizeof(T)); 
T *myobj = new(mem) T(); 

// ... 

myobj->~T(); 
operator delete(mem); 

Lưu ý rằng nếu bạn đã quá tải ::operator new cho một lớp học đặc biệt, điều này sẽ sử dụng lớp operator new, nơi bạn sử dụng new char [] sẽ bỏ qua nó.

Chỉnh sửa: mặc dù tôi nên thêm rằng tôi bỏ qua khả năng ngoại lệ tại đây. @ Mankarse của câu trả lời có vẻ (với tôi) để trang trải một phần khá tốt.

0

Trên một mức độ cơ bản, bạn đang làm điều tương tự trong cả hai tình huống: IE bạn đang instantiating một đối tượng mới trên heap và bạn 'tái phát hành bộ nhớ, nhưng trong trường hợp thứ hai bạn (rõ ràng) sử dụng toán tử vị trí mới.

Trong trường hợp này, cả hai kết quả đều mang lại kết quả tương tự, trừ đi sự khác biệt nếu hàm tạo được giải thích như Mankarse giải thích.

Ngoài ra, tôi sẽ không nói rằng bạn có thể sử dụng chúng thay thế cho nhau, vì trường hợp thứ hai không phải là một ví dụ thực tế về cách sử dụng placement new. Có thể sẽ có ý nghĩa hơn khi sử dụng placement new trong ngữ cảnh của một nhóm bộ nhớ và nếu bạn đang viết trình quản lý bộ nhớ của riêng mình để có thể đặt nhiều đối tượng trên một bộ nhớ phân bổ (trong các thử nghiệm của tôi, nó có xu hướng tiết kiệm về 25% thời gian CPU). Nếu bạn có một trường hợp sử dụng thực tế hơn cho placement new thì sẽ có rất nhiều điều bạn nên lo lắng.

0

Vâng, bạn đang phân bổ trước bộ nhớ cho đối tượng T của mình. Và nó sẽ ổn thôi. Tuy nhiên nó có ý nghĩa nếu bạn sẽ tái sử dụng khu vực được phân bổ một lần nữa. Nếu không nó sẽ chậm hơn.

0

Có. Ví dụ của bạn quá đơn giản để chứng minh điều này, nhưng bộ nhớ bạn đã phân bổ trước, "mem", nên quản lý đối tượng được lưu trữ bên trong, "myObj."

lẽ đặt một cách tốt hơn, kịch bản # 1 phân bổ không gian trên heap, và xây dựng một đối tượng trong không gian đó. Kịch bản # 2 phân bổ không gian trên heap, 'mem', sau đó xây dựng một đối tượng ở đâu đó trong không gian đó.

Bây giờ, hãy đặt một đối tượng thứ hai ở một nơi khác trong "mem". Nó trở nên phức tạp, phải không?

Việc xây dựng và tiêu hủy myObj đang xảy ra hệt trong cả hai kịch bản (trừ trường cái c ase của nhà xây dựng của bạn ném một ngoại lệ, như chỉ ra bởi Mankarse), nhưng người cấp phát đang chăm sóc quản lý bộ nhớ của bạn cho bạn trong kịch bản # 1, và không phải trong kịch bản # 2.

Vì vậy, hãy cẩn thận khi quản lý "mem" một cách thích hợp. Một common approach là như sau:

template<class T> void destroy(T* p, Arena& a) 
{ 
     if (p) { 
       p->~T();  // explicit destructor call 
       a.deallocate(p); 
     } 
} 
Các vấn đề liên quan