2010-10-18 31 views
14

Dòng thứ hai là gì? (Đã xem trong khi trả lời một câu hỏi khác.)Điều gì mới thứ hai này?

int * x = new int [1] ; 
int * y = new (x) int; 

Sau dòng thứ hai x và y có cùng giá trị (trỏ đến cùng một vị trí). Sự khác nhau giữa y = x và dòng thứ hai là gì? Nó giống như một nhà xây dựng hay cái gì đó?

+0

Lưu ý: Vị trí mới không thường được sử dụng với int (vì nó không có lợi thế (int không có hàm tạo)). –

+0

@Martin: Mặc dù 'new (x) int()' sẽ khác nhau và có thể được sử dụng thông qua một mẫu. Luôn luôn sử dụng parens, ngay cả với [] mới, là một ý tưởng tốt. –

+0

Liên kết đến câu hỏi khác mà bạn thấy điều này có thể hữu ích. –

Trả lời

13

Đó là placement new. Nó xây dựng một int mới trong bộ nhớ được trỏ đến bởi x.

Nếu bạn cố gắng:

int * x = new int [1]; 
*x = 5; 
std::cout << *x << std::endl; 
int * y = new (x) int; 
*y = 7; 
std::cout << *x << std::endl; 

đầu ra sẽ là:

5 
7 
+0

nhưng x không thay đổi nơi nó đã trỏ! –

+0

Không có gì xảy ra với 'x'. Nhưng một cái gì đó có thể xảy ra với '* x'. Hãy thử những điều sau đây: 'int * x = new int [1]; * x = 5; std :: cout << * x << std :: endl; int * y = new (x) int; * y = 7; std :: cout << * x << std :: endl; ' –

+0

@Green Code: Đúng vậy. Vị trí mới chỉ thực hiện khởi tạo. Nó giống như gọi hàm tạo cho kiểu đó trên bộ nhớ được cấp phát đã cho. –

4

Điều này được gọi là placement new. Nó cho phép bạn xây dựng một đối tượng trong bộ nhớ đã được cấp phát.

Chủ đề cũ hơn này thảo luận về where and how it is useful for.

+0

Tôi không hoàn toàn hiểu được! khi chúng ta mới một cái gì đó một lần nữa nó là con trỏ được thay đổi! nhưng làm thế nào để làm việc này mà không thay đổi con trỏ! xem đầu ra của VCEXPRESS8: x trước dòng thứ hai 0x005c4e58 x sau dòng thứ hai 0x005c4e58 là giống nhau! –

+1

Nó nói với trình biên dịch "làm các hàm tạo thông thường, nhưng thay vì tạo thêm bộ nhớ, hãy sử dụng bộ nhớ tại' x'. " Vì vậy, nó không sửa đổi 'x', nhưng sẽ ghi đè' int' mà bạn đã tạo trong dòng đầu tiên. –

+0

@Green, không có bộ nhớ mới được phân bổ với vị trí mới, đây là lý do tại sao 'y' trong ví dụ của bạn trỏ đến cùng một địa chỉ bộ nhớ là' x'. Hãy suy nghĩ về nó chỉ đơn giản là bỏ qua bước cấp phát bộ nhớ và thực hiện cuộc gọi hàm dựng thích hợp ngay lập tức, bằng cách sử dụng khối bộ nhớ được cung cấp. –

3

mới thứ hai là một "vị trí mới". Nó thực hiện khởi tạo (tức là gọi bất kỳ nhà thầu cần thiết nào) mà không thực hiện bất kỳ phân bổ nào. Nó rất hữu ích khi bạn cần tạo một lược đồ phân bổ bộ nhớ tùy chỉnh.

2
int * y = new (x) int; 

Đây là cú pháp mới theo vị trí.

EDIT: Cùng với phân bổ tùy chỉnh, vị trí mới cũng giúp tái khởi tạo trạng thái đối tượng như bên dưới.

class Test 
{ 
    int startVal; 
public: 
    Test() 
    { 
     startVal = 1; 
    } 
    void setVal(int val) { startVal = val; } 
}; 
int main() 
{ 
    Test *p = new Test; //Creates new object and initializes it with 
          //a call to constructor. 
    p->setVal(10); //Change object content. 
    new(p) Test; //Reset object: 
    //object pointed by p will be re-initialzed here by making 
    //a call to constructor. startVal will be back to 1 
} 

Như được mô tả trong các nhận xét ở trên, việc đặt lại trạng thái đối tượng cũng có thể đạt được bằng cách sử dụng vị trí mới. vị trí mới không cấp phát bộ nhớ, nó tạo đối tượng tại địa chỉ được chỉ định trong dấu ngoặc đơn.

+0

Đó không phải là một ý tưởng hay. Điều gì xảy ra nếu thử nghiệm chứa một thành viên con trỏ. Bởi vì bạn đã không gọi destructor trên đối tượng ban đầu, bạn có thể sẽ bị rò rỉ bộ nhớ. Nếu bạn định sử dụng vị trí mới để đặt lại giá trị (thường là ý tưởng tồi), bạn phải gọi hàm hủy trên đối tượng trước (bạn có thể gọi hàm hủy theo cách thủ công thông qua con trỏ). –

+0

@Martin: Tốt. Nhưng tôi thấy thiết lập lại được sử dụng trong mã thương mại được sử dụng rộng rãi của chúng tôi. Họ không có bất kỳ con trỏ bên trong lớp học. Không chắc liệu đó có phải là thực hành không tốt hay không. Dù sao, cảm ơn bạn. – bjskishore123

3

Đây là vị trí mới.

Mặc dù bạn không thường sử dụng nó với các loại số nguyên.
Nó thường được sử dụng để xây dựng một bộ đệm nơi bạn sau đó xây dựng các loại khác vào.

// Allocate a buffer with enough room for two T objects. 
char* buffer = new char[sizeof(T) * 2]; 

// Allocate a T in slot zero 
T* t1 = new (buffer + 0 * sizeof(T)) T("Zero"); 

// Allocate a T in slot one 
T* t2 = new (buffer + 1 * sizeof(T)) T("One"); 

Thats cơ bản.
Nhưng hãy nhớ rằng các đối tượng được phân bổ với vị trí mới không thể bị xóa với câu lệnh delete. Điều này là do delete cố gắng đòi lại bộ nhớ được phân bổ bởi new (cũng như gọi hàm hủy). Vì vậy, để sử dụng các đối tượng này một cách chính xác, bạn phải tự gọi đó là destructor.

t1->~T(); 
t2->~T(); 

Đừng quên xóa bộ đệm gốc.

delete [] buffer; 

Một vài hãy cẩn thận khác:
Mọi người thường thấy đệm có thể được thực hiện trên stack và do đó được tự động giải phóng

char buffer[sizeof(T) * 2]; 

Đáng tiếc là điều này có thể về mặt kỹ thuật OK (Nó biên dịch). Nhưng nó không được đảm bảo để làm việc như bộ nhớ của bộ đệm có thể không được liên kết chính xác cho một T được đặt bên trong. Vì vậy, bạn phải phân bổ bộ đệm động (bằng cách sử dụng mới nó gurantees rằng bộ nhớ được sắp xếp chính xác cho bất kỳ đối tượng nào của kích thước được phân bổ (do đó, nó cũng được sắp xếp cho bất kỳ kích thước nhỏ hơn sau đó kích thước được phân bổ). vấn đề này là sử dụng một std :: vector

std::vector<char> buffer(sizeof(T) * 2); 
T* t1 = new (&buffer[0] + 0 * sizeof(T)) T("Zero"); 
T* t2 = new (&buffer[0] + 1 * sizeof(T)) T("One"); 

một sử dụng các vị trí mới là để thiết lập lại một đối tượng
tôi đã thấy điều này được thực hiện nhưng tôi thích sử dụng toán tử gán chuẩn hơn:.

T obj1("Plop"); 
obj1 = T("Another Plop"); 

// Can be done like this: 
T obj1("Plop"); 
obj1.~T(); 
new (&obj1) T("Another Plop"); // Seems excessive to me. But can be us-full 
           // in some extreme situations. 

Hãy nhớ nếu bạn sử dụng phương pháp đặt lại, trước tiên bạn phải hủy đối tượng cũ (hoặc đối tượng có thể không hoạt động chính xác).

Các vấn đề liên quan