2012-10-15 19 views
6

Tôi biết rằng memset được cau mày khi khởi tạo class. Ví dụ, một cái gì đó như sau:Làm thế nào để thực hiện tương đương với memset (điều này, ...) mà không có clobbering vtbl?

class X { public: 
X() { memset(this, 0, sizeof(*this)) ; } 
... 
} ; 

sẽ clobber các vtbl nếu có một chức năng virtual trong hỗn hợp.

Tôi đang làm việc trên một codebase cũ (humongous) là C-ish nhưng được biên dịch bằng C++, vì vậy tất cả các thành viên được đề cập thường là POD và không yêu cầu các nhà xây dựng C++ truyền thống. Việc sử dụng C++ dần dần leo lên (như các hàm ảo), và điều này khiến các nhà phát triển không nhận ra rằng memset có những hàm răng C++ bổ sung này.

Tôi tự hỏi nếu có một cách an toàn C++ để thực hiện khởi tạo không bắt đầu tất cả bằng không, có thể được theo sau bởi khởi tạo thành viên cụ thể khi khởi tạo không phù hợp?

Tôi tìm thấy các câu hỏi tương tự memset for initialization in C++zeroing derived struct using memset. Cả hai đều có "không sử dụng memset()" câu trả lời, nhưng không có lựa chọn thay thế tốt (đặc biệt là cho các cấu trúc lớn có khả năng chứa nhiều thành viên nhiều).

+2

Thông thường bạn lấy địa chỉ của trường đầu tiên và xóa từ đó. Không nghi ngờ điều này vi phạm một số quy tắc, nhưng nó luôn luôn làm việc cho tôi. –

+0

Tùy chọn khác là viết thói quen phân bổ của riêng bạn cho lớp, vì vậy bạn có thể đảm bảo không gian được xóa, nhưng IIRC có những hạn chế xung quanh đó. –

+0

Bạn không có nghĩa là 'memset (this, 0, sizeof * this)'? (Và vâng, điều đó cũng sẽ che dấu vtbl.) –

Trả lời

1

Bạn luôn có thể thêm các hàm tạo vào các cấu trúc được nhúng này, vì vậy chúng tự rõ ràng để nói.

+0

Câu hỏi thực sự là về những gì cần đặt trong hàm tạo (không phải là, ví dụ, 100 câu lệnh gán). –

1

Điều này thật ghê gớm, nhưng bạn có thể quá tải operator new/delete cho các đối tượng này (hoặc trong một lớp cơ sở chung) và thực hiện cung cấp bộ đệm không có. Một cái gì đó như thế này:

class HideousBaseClass 
{ 
public: 
    void* operator new(size_t nSize) 
    { 
     void* p = malloc(nSize); 
     memset(p, 0, nSize); 
     return p; 
    } 
    void operator delete(void* p) 
    { 
     if(p) 
      free(p); 
    } 
}; 

Một cũng có thể ghi đè lên toàn cầu new/delete nhà khai thác, nhưng điều này có thể có tác động tiêu cực về hiệu suất.

Chỉnh sửa: Tôi vừa nhận ra rằng cách tiếp cận này sẽ không hoạt động đối với các đối tượng được phân bổ ngăn xếp.

+1

không phải là _completely_ hideous ... – AShelly

4

Đối với mỗi lớp nơi bạn tìm thấy một cuộc gọi memset, hãy thêm một hàm thành viên memset bỏ qua đối số con trỏ và kích thước và gán cho tất cả các thành viên dữ liệu.

chỉnh sửa: Trên thực tế, nó không nên bỏ qua các con trỏ, nó sẽ so sánh nó với this. Trên một trận đấu, làm điều đúng cho đối tượng, trên một không khớp, định tuyến lại chức năng toàn cục.

+1

+1 cho sự thông minh –

+0

Vâng, có một số công đức cho điều đó. Dễ nhớ hơn để giữ cho nó cập nhật hơn để quản lý danh sách trong 5 nhà xây dựng khác nhau. –

1

Hãy thử điều này:

template <class T> 
void reset(T& t) 
{ 
    t = T(); 
} 

này sẽ zeroed đối tượng của bạn - bất kể đó là POD hay không.

Nhưng đừng làm điều này:

A::A() { reset(*this); } 

này sẽ gọi A::A trong đệ quy vô hạn !!!

Hãy thử điều này:

struct AData { ... all A members }; 
    class A { 
    public: 
     A() { reset(data); } 
    private: 
     AData data; 
    }; 
+0

Đây có phải là điều tăng giá trị_initialized: http://www.boost.org/doc/libs/1_38_0/libs/utility/value_init.htm đang hoạt động? –

+0

@PeeterJoot Nhiều hơn hoặc ít hơn. Trên thực tế nó làm theo cách phức tạp hơn, Thư viện tăng này cho rằng do một số vấn đề trình biên dịch và vì cách trình bày trong câu trả lời của tôi không phải luôn là cách hiệu quả nhất cho các loại không POD, nó sử dụng biến cố định tĩnh cho mục đích này. Tuy nhiên nếu bạn tìm kiếm thay thế memset - cách "đơn giản hóa" của tôi là đủ. – PiotrNycz

+0

't = T();' điều này sẽ không là đối tượng, nó sẽ thay thế nó bằng một đối tượng được xây dựng mặc định. Nếu hàm tạo mặc định không khởi tạo thành viên, thành viên đó vẫn sẽ không được khởi tạo. Nếu nó khởi tạo một cái gì đó khác 0, nó cũng sẽ được để lại ở giá trị đó. Đặt lại thành _default_ thay vì _all zeros_ có thể thực sự là một điều tốt, nhưng chúng không giống như bạn đề xuất. – Shahbaz

0

Bạn có thể sử dụng con trỏ số học để tìm ra hàng loạt các byte bạn muốn không ra:

class Thing { 
public: 
    Thing() { 
     memset(&data1, 0, (char*)&lastdata - (char*)&data1 + sizeof(lastdata)); 
    } 
private: 
    int data1; 
    int data2; 
    int data3; 
    // ... 
    int lastdata; 
}; 

(Edit: Tôi ban đầu được sử dụng offsetof() cho điều này, nhưng một bình luận đã chỉ ra rằng điều này chỉ được phép làm việc trên POD, và sau đó tôi nhận ra rằng bạn chỉ có thể sử dụng trực tiếp các địa chỉ thành viên.)

+0

'offsetof' chỉ đảm bảo hoạt động trên các loại _POD_ –

0

Giải pháp tốt hơn tôi có thể thứ hai là tạo một cấu trúc riêng biệt, nơi bạn sẽ đặt các thành viên phải được ghi nhớ về không. Không chắc chắn nếu thiết kế này phù hợp với bạn.

Cấu trúc này không có vtable và mở rộng thông báo. Nó sẽ chỉ là một đoạn dữ liệu. Bằng cách này, việc ghi nhớ cấu trúc là an toàn.

Tôi đã thực hiện một ví dụ:

#include <iostream> 
#include <cstring> 

struct X_c_stuff { 
    X_c_stuff() { 
     memset(this,0,sizeof(this)); 
    } 
    int cMember; 
}; 
class X : private X_c_stuff{ 
public: 
    X() 
    : normalMember(3) 
    { 
     std::cout << cMember << normalMember << std::endl; 
    } 
private: 
    int normalMember; 
}; 

int main() { 
    X a; 
    return 0; 
} 
1

đòn bẩy thực tế là một trường hợp tĩnh được khởi tạo vào zero: https://ideone.com/GEFKG0

template <class T> 
struct clearable 
{ 
    void clear() 
    { 
     static T _clear; 
     *((T*)this) = _clear; 
    }; 
}; 

class test : public clearable<test> 
{ 
    public: 
     int a; 
}; 

int main() 
{ 
    test _test; 
    _test.a=3; 
    _test.clear(); 

    printf("%d", _test.a); 

    return 0; 
} 

Tuy nhiên ở trên sẽ gây ra các nhà xây dựng (của lớp templatised) được gọi là lần thứ hai.

Đối với một giải pháp mà không gây gọi ctor này có thể được sử dụng thay cho: https://ideone.com/qTO6ka

template <class T> 
struct clearable 
{ 
    void *cleared; 
    clearable():cleared(calloc(sizeof(T), 1)) {} 

    void clear() 
    { 
     *((T*)this) = *((T*)cleared); 
    }; 
}; 

... và nếu bạn đang sử dụng C++ 11 trở đi sau có thể được sử dụng: https://ideone.com/S1ae8G

template <class T> 
struct clearable 
{ 
    void clear() 
    { 
     *((T*)this) = {}; 
    }; 
}; 
Các vấn đề liên quan