2010-03-02 24 views
31

Tôi đang quá tải operator new, nhưng gần đây tôi đã gặp sự cố với căn chỉnh. Về cơ bản, tôi có một lớp học IBase cung cấp operator newdelete trong tất cả các biến thể bắt buộc. Tất cả các lớp bắt nguồn từ IBase và do đó cũng sử dụng các trình phân bổ tùy chỉnh.điều hành quá tải mới và căn chỉnh

Vấn đề tôi đang gặp phải bây giờ là tôi có một con Foo phải được căn chỉnh 16 byte, trong khi tất cả những thứ khác đều ổn khi được căn chỉnh đến 8 byte. Tuy nhiên, bộ cấp phát bộ nhớ của tôi chỉ liên kết với các ranh giới 8 byte theo mặc định, vì vậy bây giờ mã trong IBase::operator new trả về một phần bộ nhớ không sử dụng được. Làm thế nào điều này nghĩa vụ phải được giải quyết một cách chính xác?

Tôi chỉ có thể buộc tất cả phân bổ đến 16 byte, sẽ hoạt động tốt cho đến khi loại căn chỉnh 32 byte bật lên. Tìm ra sự liên kết bên trong operator new dường như không tầm thường (tôi có thể thực hiện một cuộc gọi hàm ảo ở đó để có được sự liên kết thực tế không?) Cách được đề nghị để xử lý điều này là gì?

Tôi biết malloc có nghĩa vụ trả lại một phần bộ nhớ phù hợp cho mọi thứ, không may, "mọi thứ" này không bao gồm các loại SSE và tôi thực sự muốn làm việc này mà không yêu cầu người dùng nhớ loại nào có liên kết.

+0

Có lẽ bạn có thể sử dụng IBase (IBase16?) Khác cho một số đối tượng có yêu cầu căn chỉnh đặc biệt. –

+0

Bạn thậm chí có thể làm cho lớp cơ sở này (IBase16, IBase32) templated, vì vậy bạn có thể sử dụng IBase . – Patrick

+0

Bạn có thể căn chỉnh trên ranh giới 64 byte. :) – Bill

Trả lời

20

Đây là giải pháp khả thi. Nó sẽ luôn luôn chọn các nhà điều hành với sự liên kết cao nhất trong một hệ thống phân cấp đưa ra:

#include <exception> 
#include <iostream> 
#include <cstdlib> 

// provides operators for any alignment >= 4 bytes 
template<int Alignment> 
struct DeAllocator; 

template<int Alignment> 
struct DeAllocator : virtual DeAllocator<Alignment/2> { 
    void *operator new(size_t s) throw (std::bad_alloc) { 
    std::cerr << "alignment: " << Alignment << "\n"; 
    return ::operator new(s); 
    } 

    void operator delete(void *p) { 
    ::operator delete(p); 
    } 
}; 

template<> 
struct DeAllocator<2> { }; 

// ........... Test ............. 
// different classes needing different alignments 
struct Align8 : virtual DeAllocator<8> { }; 
struct Align16 : Align8, virtual DeAllocator<16> { }; 
struct DontCare : Align16, virtual DeAllocator<4> { }; 

int main() { 
    delete new Align8; // alignment: 8 
    delete new Align16; // alignment: 16 
    delete new DontCare; // alignment: 16 
} 

Nó dựa trên các quy tắc thống trị: Nếu có một sự nhập nhằng trong tra cứu, và sự nhập nhằng giữa tên của một nguồn gốc và cơ sở ảo lớp, tên của lớp dẫn xuất được lấy thay thế.


Câu hỏi được nêu lý do tại sao DeAllocator<I> kế thừa DeAllocator<I/2>. Câu trả lời là bởi vì trong một hệ thống phân cấp nhất định, có thể có các yêu cầu căn chỉnh khác nhau được áp đặt bởi các lớp. Hãy tưởng tượng rằng IBase không có yêu cầu liên kết, A có yêu cầu 8 byte và B có 16 yêu cầu byte và được thừa hưởng A:

class IBAse { }; 
class A : IBase, Alignment<8> { }; 
class B : A, Alignment<16> { }; 

Alignment<16>Alignment<8> cả phơi bày một operator new. Nếu bây giờ bạn nói new B, trình biên dịch sẽ tìm kiếm operator new trong B và sẽ tìm hai chức năng:

  // op new 
      Alignment<8>  IBase 
       ^  /
        \  /
        \ /
// op new   \/
Alignment<16>   A 
      \  /
       \ /
       \/
       B 

B ->  Alignment<16> -> operator new 
B -> A -> Alignment<8> -> operator new 

Như vậy, đây sẽ là mơ hồ và chúng tôi sẽ không biên dịch: Không phải của các ẩn khác một. Nhưng nếu bây giờ bạn thừa hưởng Alignment<16> hầu như từ Alignment<8> và làm AB kế thừa họ hầu như, các operator new trong Alignment<8> sẽ được ẩn:

  // op new 
      Alignment<8>  IBase 
       ^  /
       /\  /
      / \ /
// op new/  \/
Alignment<16>   A 
      \  /
       \ /
       \/
       B 

quy tắc ẩn đặc biệt này (còn gọi là thống trị rule) tuy nhiên chỉ hoạt động nếu tất cả các đối tượngAlignment<8> đều giống nhau. Vì vậy, chúng tôi luôn kế thừa hầu như: Trong trường hợp đó, chỉ có một đối tượngAlignment<8> (hoặc 16, ...) hiện có trong bất kỳ phân cấp lớp nào đã cho.

+0

Sử dụng tốt các mẫu và tôi thích cách bạn nghĩ, nhưng tôi phải thiếu một chút rõ ràng vì tôi không thấy cách căn chỉnh đang được thực hiện. Giả sử rằng các cấu trúc sẽ được đóng gói bằng cách sử dụng các kỹ thuật được cung cấp bởi bất kỳ trình biên dịch nào mà OP đang sử dụng? –

+0

@John, trong mẫu này, căn chỉnh được chỉ định bằng cách in giá trị của nó. Bạn sẽ chuyển số nguyên thành 'posix_memalign' hoặc một cái gì đó. @ Anteru sẽ tìm ra cách để làm điều đó, tôi nghi ngờ. –

+1

Tôi không thấy cách liên kết thu được ở đây. Có thể bạn không thực sự căn chỉnh ở đây (chỉ hiển thị cách bạn có thể sử dụng các mẫu để chỉ ra kích thước liên kết có thể), nhưng trong trường hợp đó, bạn sẽ không có được cùng một lớp templated bình thường (không kế thừa từ một lớp templated khác), nơi Align16 kế thừa từ DeAllocator <16> và Align8 kế thừa từ DeAllocator <8> (và không có nhiều kế thừa)? – Patrick

7

kết hợp là cách tiếp cận phù hợp, tuy nhiên quá tải toán tử mới thì không. Điều này sẽ thực hiện những gì bạn cần:

__declspec(align(256)) struct cachealign{}; 
__declspec(align(4096)) struct pagealign{}; 
struct DefaultAlign{}; 
struct CacheAlign:private cachealign{}; 
struct PageAlign: CacheAlign,private pagealign{}; 

void foo(){ 
DefaultAlign d; 
CacheAlign c; 
PageAlign p; 
std::cout<<"Alignment of d "<<__alignof(d)<<std::endl; 
std::cout<<"Alignment of c "<<__alignof(c)<<std::endl; 
std::cout<<"Alignment of p "<<__alignof(p)<<std::endl; 
} 

Prints

Alignment of d 1 
Alignment of c 256 
Alignment of p 4096 

Đối với gcc, sử dụng

struct cachealign{}__attribute__ ((aligned (256))); 

Lưu ý rằng có lựa chọn tự động của sự liên kết lớn nhất, và điều này làm cho các đối tượng đặt trên stack, những cái mới, và là thành viên của các lớp khác. Nó cũng không thêm bất kỳ ảo và giả sử EBCO, không có kích thước thêm cho lớp (bên ngoài của padding cần thiết cho sự liên kết chính nó).

1

Sử dụng Visual Studio Express 2010, ví dụ trên dường như không làm việc với mới:

CacheAlign *c = new CacheAlign; 

sẽ cho __align_of (c) == 4 (để được mong đợi tôi đoán), nhưng địa chỉ của một thành viên đầu tiên của CacheAlign không được căn chỉnh theo yêu cầu.

Không phải là một câu hỏi gần đây, nhưng nếu tôi hiểu OP chính xác, anh ấy có các lớp là con của lớp cha xác định trình phân bổ & và tất cả có thể yêu cầu căn chỉnh cụ thể. Có gì sai khi có một lệnh mới đơn giản gọi một số phân bổ cá nhân thực hiện công việc thực tế và nhận đối số căn chỉnh - một phiên bản chung với căn chỉnh mặc định trong lớp cha được kế thừa hoặc quá tải với phiên bản chỉ định căn chỉnh chính xác?

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