2010-09-09 18 views
17

C++ cho phép quá tải operator new - cả toàn cầu và mỗi lớp - thông thường operator new, operator new[] được sử dụng với tuyên bố new[] và vị trí operator new riêng biệt.Làm thế nào tôi có thể quá tải một cách hợp lý toán tử vị trí mới?

Hai phần trước đây trong số ba giá trị này thường bị quá tải để sử dụng trình phân bổ tùy chỉnh và thêm lần truy tìm. Nhưng vị trí operator new có vẻ khá đơn giản - nó thực sự không có gì bên trong. Ví dụ: trong Visual C++, việc triển khai mặc định chỉ trả về địa chỉ được chuyển vào cuộc gọi:

//from new.h 
inline void* operator new(size_t, void* where) 
{ 
    return where; 
} 

Còn điều gì khác? Tại sao và làm cách nào tôi có thể quá tải vị trí một cách hợp lý operator new?

+0

Bạn có yêu cầu sử dụng quá tải toán tử mới không? Đó là một câu hỏi khá rộng. –

+3

@Alexander Rafferty Tôi hỏi cụ thể về cách sử dụng vị trí quá tải mới. Tôi không thể thấy bất kỳ mục đích nào trong trường hợp cụ thể này. – sharptooth

+0

+1 để truy cập tệp tiêu đề. – Chubsdad

Trả lời

13

Câu trả lời đúng là bạn không thể thay thế vị trí nhà điều hành mới.

§18.4.3 Các mẫu đăng ký
Các chức năng này được bảo lưu, chương trình C++ không thể xác định các hàm thay thế các phiên bản trong thư viện Chuẩn C++.

Lý do: Mục đích duy nhất của các nhà điều hành phân bổ và deallocation là phân bổ và deallocate bộ nhớ, do đó, khi cho bộ nhớ không có gì nên được thực hiện. (Tiêu chuẩn đặc biệt lưu ý rằng các chức năng này "Cố ý thực hiện không có hành động khác.")

+0

Chà. Visual C++ 9 vui vẻ cho phép điều đó. – sharptooth

+0

@sharptooth: Chương trình thử nghiệm của bạn là gì? Không ngạc nhiên, nó là bất hợp pháp trong cùng một cách nó là bất hợp pháp để thêm những thứ (nói chung) vào không gian tên 'std'. Bản thân ngôn ngữ không phải là khôn ngoan hơn, nhưng thư viện chuẩn cấm nó. – GManNickG

+0

Tôi đã thay thế toán tử void * mới [] (size_t, void * ở đâu) bằng cách vào các tệp tiêu đề của trình biên dịch của tôi. Nó hoạt động tốt. Trên thực tế, tôi đã không thay thế nó - tôi đã xóa nó, để làm cho nó không sử dụng được, bởi vì nó ** là ** không sử dụng được. –

3

One example có tại Câu hỏi thường gặp của Stroustrup.

+1

Thành thật mà nói tôi không có ví dụ đó. – sharptooth

+0

@sharptooth Tìm thấy một ví dụ tương tự với một số giải thích bổ sung [tại đây] (http://www.parashift.com/c++-faq/memory-pools.html) (mặc dù có một số điểm không chính xác) – blgt

4

Về mặt kỹ thuật, một vị trí operator new là bất kỳ operator new có các đối số bổ sung ngoài kích thước bộ nhớ cần thiết.

Vì vậy, new(std::nothrow) X sử dụng vị trí operator new và do đó, new(__FILE__, __LINE__) X.

Lý do duy nhất để ghi đè số operator new(size_t, void*) có thể là thêm thông tin truy tìm, nhưng tôi nghĩ rằng nhu cầu cho điều đó sẽ khá thấp.

+3

Một lý do để thực hiện nó có thể buộc phải sử dụng một bộ cấp phát bộ nhớ cụ thể; điều này đã đưa ra cho chúng ta khi làm những điều phức tạp trên Windows, nơi nó là cần thiết để buộc việc sử dụng các cấp phát được sử dụng bởi một DLL cụ thể hơn là một số khác. Tất cả đều hoạt động, miễn là mã khớp với 'new' từ một lib với' delete' từ cùng một lib.) –

+0

Câu đầu tiên là sai, vị trí mới có ý nghĩa cụ thể. Và vị trí mới không thể bị quá tải hoặc thay thế. – GManNickG

+0

@GMan: Bạn nói đúng. Dường như không có tên cụ thể cho các toán tử 'operator new' có tham số bổ sung, mặc dù chúng thường được gọi bằng cú pháp vị trí. –

-1

Cách sử dụng chính của tôi là tạo một mảng lớn các đối tượng. Hoạt động của nó tốt hơn nhiều và có ít chi phí hơn để phân bổ bộ nhớ trong một khối hoàn toàn, tức là sử dụng VirtualAlloc từ Win32 (khi các cửa sổ lập trình). Sau đó, bạn chỉ cần vượt qua một ptr trong khối đó cho mỗi đối tượng vị trí mới như:

char *cp = new char[totalSize]; 

for(i = 0; i < count; i++, cp += ObjSize)   
{               
    myClass *obj = new(cp) myClass;    
} 
+1

Đó là sử dụng, không quá tải ... –

+0

IIUC, anh ta không yêu cầu các trường hợp sử dụng của chính nhà điều hành vị trí mới, nhưng đối với các trường hợp sử dụng của * quá tải * nó. –

1

Ghi đè rõ ràng nhất sẽ là sao chép việc triển khai này.

Một điều hợp lý khác là thêm một số kiểm tra (ví dụ: xác minh rằng không có "điểm đánh dấu bị ràng buộc" trong vùng yêu cầu). Tuy nhiên, tôi nghĩ rằng điểm nhiều hơn bạn phải ghi đè lên nó, ngay sau khi bạn ghi đè lên những người khác (đối với một lớp nhất định), vì cơ chế của tên tra cứu (hoặc không ghi đè nó để ngăn chặn việc sử dụng nó). Page 5 , điều đó cũng tốt, nhưng đó là một quyết định có ý thức).

0

Tôi đã nhìn thấy một ví dụ mà hai đối số mới [] bị ghi đè để trả về các khối bộ nhớ được điền trước bằng char được chuyển làm đối số bổ sung. Tôi không nhớ mã ban đầu được sử dụng (có thể là memset()), nhưng nó có chức năng như sau:

#include <iostream> 
#include <algorithm> 
#include <new> 
void* operator new [](size_t n, char c) 
{ 
     char* p = new char[n]; 
     std::fill(p, p+n, c); 
     return p; 
} 
int main() 
{ 
     char* p = new('a') char[10]; 
     std::cout << p[0] << p[1] << ".." << p[9] << '\n'; 
} 

mặc dù tôi đoán điều này sẽ không được gọi là "vị trí" mới vì nó không hoạt động vị trí. Nó có thể có thể hữu ích nếu templated để nó có thể xây dựng mảng của bất kỳ loại, đầy với một bản sao của đối tượng thông qua như đối số thứ hai của nó ... nhưng sau đó, chúng tôi có container cho anyway.

+0

Nó được gọi là vị trí mới, ví dụ: tiêu chuẩn này cho biết: "Giá trị trên có thể được áp dụng trong tất cả các mảng * biểu thức mới *, bao gồm cả tham chiếu hàm thư viện' toán tử new [] (std :: size_t, void *) '** và các hàm phân bổ vị trí khác. * * " –

0

Tôi không chắc chắn chính xác của câu hỏi, nhưng ghi đè vị trí sau mới ở cấp lớp:

struct Bar { 
void* operator new(size_t /* ignored */, void* where) throw() { return where; } 
}; 

int main() { 
    char mem[1]; 
    Bar* bar = new(mem) Bar; 
} 

tôi tin rằng đây là hợp pháp C++ (và biên dịch và chạy tốt với gcc 4.4.6).

Bạn được tự do thay đổi việc thực hiện toán tử này khi bạn thấy phù hợp (bao gồm xóa mệnh đề throw(), điều này có nghĩa là trình biên dịch không còn kiểm tra con trỏ where cho null trước khi gọi hàm tạo). Tread cẩn thận mặc dù.

§18.4 1.3 thú vị. Tôi tin rằng điều này chỉ áp dụng cho các nhà điều hành toàn cầu chức năng mới, không phải lớp học cụ thể những người thân.

+1

Câu hỏi đặt ra là liệu có thể thực hiện bất kỳ triển khai nào khác không. Tôi có thể làm gì khác ngoại trừ 'trở về đâu; '? – sharptooth

+0

Ah, tôi hiểu rồi. Vâng, bạn có thể kiểm tra để đảm bảo rằng địa chỉ này chưa được sử dụng, không chồng chéo với bộ nhớ đã cấp phát hoặc thậm chí bạn có thể điều chỉnh 'where' để được căn chỉnh tốt hơn (trả về' trong đó + n' byte). Không ai trong số này tôi từng thấy trước đây trong thực tế. –

0

Chức năng bổ sung quan trọng nhất cho vị trí quá tải mới sẽ là kiểm tra căn chỉnh địa chỉ.

Ví dụ: giả sử một số lớp yêu cầu căn chỉnh 16 byte. Nhà phát triển quá tải mới, mới [], xóa và xóa [] - chỉ để đảm bảo mọi thứ được căn chỉnh đúng cách.

Mọi thứ hoạt động tốt vào thời điểm khi anh ta cố gắng sử dụng lớp học của mình với thư viện sử dụng vị trí mới ... Thư viện không có ý tưởng nếu/cần căn chỉnh gì cho lớp và địa chỉ cố gắng "đặt" đối tượng vào có thể không được căn chỉnh - bùng nổ lớn.

Ví dụ đơn giản nhất về tình huống như vậy - hãy thử sử dụng tiêu chuẩn :: vector <T> trong đó T yêu cầu căn chỉnh không chuẩn.

Quá tải cho vị trí mới cho phép phát hiện nếu con trỏ không được căn chỉnh - có thể tiết kiệm thời gian gỡ lỗi.

1

Để xác định quản lý bộ nhớ của riêng bạn cho một khu vực được giám sát là một cách sử dụng tốt.

Để có các chế độ xem khác nhau trên cùng một dữ liệu vật lý (không cần di chuyển dữ liệu) là sử dụng giao nhau khác. Nó cũng cho phép bạn đọc một tệp có cấu trúc làm ký tự trên một bộ đệm và sau đó, sự chồng chất của cấu trúc lôgic của chúng bằng cách xác định một đối tượng của lớp đó trên bộ đệm. Sự kết hợp của điều này với bản đồ bộ nhớ của các tập tin, có thể cung cấp những cải tiến lớn trong hiệu suất. Phần cứng được ánh xạ bộ nhớ ... Vì vậy, hàng ngàn ứng dụng!

+1

Bạn có thể vui lòng cung cấp một ví dụ không? Câu hỏi này không phải là về cách sử dụng vị trí mới từ mã người dùng, đó là về việc quá tải toán tử. – sharptooth

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