2010-01-06 25 views
9

Giả sử tôi có một lớp học nhưKhông gọi hàm tạo của một lớp trống thực sự sử dụng bất kỳ bộ nhớ nào?

class Empty{ 
    Empty(int a){ cout << a; } 
} 

Và sau đó tôi gọi nó bằng cách sử

int main(){ 
    Empty(2); 
    return 0; 
} 

này có gây ra bất kỳ bộ nhớ được cấp phát trên stack cho việc tạo ra một đối tượng "Empty"? Rõ ràng, các đối số cần được đẩy lên ngăn xếp, nhưng tôi không muốn chịu thêm bất kỳ chi phí nào. Về cơ bản tôi đang sử dụng constructor như là một thành viên tĩnh.

Lý do tôi muốn làm điều này là do các mẫu. Mã thực tế trông giống như

template <int which> 
class FuncName{ 
    template <class T> 
    FuncName(const T &value){ 
     if(which == 1){ 
      // specific behavior 
     }else if(which == 2){ 
      // other specific behavior 
     } 
    } 
}; 

cho phép tôi để viết một cái gì đó giống như

int main(){ 
    int a = 1; 
    FuncName<1>(a); 
} 

vì vậy mà tôi có được chuyên môn hóa một số mẫu, trong khi không phải chỉ định loại T. Ngoài ra, tôi hy vọng trình biên dịch sẽ tối ưu hóa các nhánh khác đi bên trong constructor. Nếu bất cứ ai biết nếu điều này là đúng hay làm thế nào để kiểm tra, điều đó sẽ được đánh giá cao. Tôi cũng giả định rằng việc ném các mẫu vào tình huống không làm thay đổi vấn đề "lớp trống" ở trên, đúng không?

+0

Câu hỏi đặt ra là tại sao bạn quan tâm.Đó là công việc của trình biên dịch để chăm sóc và tạo ra mã tốt nhất. Bạn nên tập trung vào việc viết mã biểu cảm nhất. –

+0

PS. Không có yêu cầu cho đối số được đẩy lên ngăn xếp. C++ ABI được xác định không chính xác để các trình biên dịch có opertunity sử dụng đăng ký để chuyển các tham số nếu điều đó làm cho mã hiệu quả hơn –

+0

Tôi quan tâm vì tôi muốn có hiệu suất cao nhất mà tôi có thể nhận được; Tôi thực sự ghét thái độ mã nên thanh lịch và không phải lo lắng về những điều này. Đôi khi, những điều này quan trọng (tôi làm tính toán hiệu suất cao). –

Trả lời

16

Trích dẫn Stroustrup:

Tại sao kích thước của một lớp trống không phải là 0? Để đảm bảo rằng địa chỉ của hai đối tượng khác nhau sẽ khác nhau. Đối với cùng một lý do, "mới" luôn trả về con trỏ cho các đối tượng riêng biệt. Xem xét:

class Empty { }; 

void f() 
{ 
    Empty a, b; 
    if (&a == &b) cout << "impossible: report error to compiler supplier"; 

    Empty* p1 = new Empty; 
    Empty* p2 = new Empty; 
    if (p1 == p2) cout << "impossible: report error to compiler supplier"; 
} 

Có một quy tắc thú vị mà nói rằng một lớp cơ sở có sản phẩm nào không cần phải được đại diện bởi một byte riêng biệt:

struct X : Empty { 
    int a; 
    // ... 
}; 

void f(X* p) 
{ 
    void* p1 = p; 
    void* p2 = &p->a; 
    if (p1 == p2) cout << "nice: good optimizer"; 
} 

tối ưu hóa này là an toàn và có thể là hữu ích nhất. Nó cho phép một lập trình viên sử dụng các lớp trống để biểu diễn các khái niệm rất đơn giản mà không cần phải trả phí. Một số trình biên dịch hiện tại cung cấp "tối ưu hóa lớp cơ sở trống" này.

+0

Nhưng nếu đối tượng được tạo ra không bao giờ được tham chiếu thì sao? Trình biên dịch có cho phép dự trữ không có không gian ngăn xếp cho nó, hoặc ngay lập tức làm sạch nó ngay cả khi nó không đi ra khỏi phạm vi? –

+0

Có, tôi chắc chắn rằng trình biên dịch sẽ loại bỏ toàn bộ bộ nhớ dành riêng nếu nó không được sử dụng hoặc tham chiếu. Đó là một tối ưu hóa khá đơn giản. Tùy thuộc vào trình biên dịch, tất nhiên. –

+1

Chừng nào ứng xử không bị thay đổi thì trình biên dịch được tự do loại bỏ toàn bộ khái niệm của đối tượng dưới mui xe và toàn bộ điều có thể được gạch vào mã và tất cả các giá trị được lưu trong sổ đăng ký. –

2

Có thể, không, tùy thuộc vào hoàn cảnh. Nếu bạn nói:

Empty e; 
Empty * ep = & e; 

thì rõ ràng mọi thứ phải được phân bổ.

+0

Trình biên dịch có nhất thiết phải phát ra mã _allocate_ một cái gì đó để cung cấp cho nó một địa chỉ không? Có lý do tại sao & e không thể là một địa chỉ mà không phải là đống cũng không ngăn xếp, và nơi không có đối tượng thực tế? – James

+0

@Autopulated: Tôi tin như vậy, xem bình luận của Richard Pennington. Câu hỏi thú vị hơn là nếu bạn không bao giờ yêu cầu địa chỉ của nó; sau đó nó vẫn phải có một? (Một câu hỏi rất giống zen) –

+1

Nếu bạn lấy địa chỉ của e, và sau đó làm một cái gì đó với địa chỉ đó (mà mã ví dụ của tôi không) sau đó e phải được instantiated - tức là phân bổ. –

2

Hãy thử và xem. Nhiều trình biên dịch sẽ loại bỏ các đối tượng tạm thời như vậy khi được yêu cầu tối ưu hóa đầu ra của chúng.

Nếu tháo là quá phức tạp, sau đó tạo ra hai chức năng với số lượng khác nhau của các đối tượng như vậy và xem nếu có bất kỳ sự khác biệt trong các địa điểm ngăn xếp của các đối tượng xung quanh họ, một cái gì đó như:

void empty1 (int x) 
{ 
    using namespace std; 

    int a; 
    Empty e1 (x); 
    int b; 

    cout << endl; 
    cout << "empty1" << endl; 
    cout << hex << int (&x) << " " << dec << (&x - &a) << endl; 
    cout << hex << int (&a) << " " << dec << (&a - &b) << endl; 
} 

và sau đó thử chạy nó so với hàm empty8 với tám ô trống được tạo. Với g ++ trên x86, nếu bạn lấy địa chỉ của bất kỳ ô trống nào, bạn sẽ có được vị trí giữa x và a trên ngăn xếp, do đó bao gồm x ở đầu ra. Bạn không thể giả định rằng lưu trữ cho các đối tượng sẽ kết thúc theo thứ tự giống như chúng được khai báo trong mã nguồn.

+0

Bằng cách nào? Tôi đang xem danh sách assembly g ++ cho một ví dụ đơn giản và rất khó để làm theo. Tôi đang sử dụng 'g ++ -c -g -O2 -Wa, -ahl = file.s file.cpp' –

+0

Bạn có thể biên dịch tốt hơn để thực thi (với thông tin gỡ lỗi), sau đó sử dụng' objdump -dS' và tìm kiếm các dòng nguồn mà bạn quan tâm. Vì bạn đã tối ưu hóa, hãy nhớ rằng cùng một dòng nguồn chịu trách nhiệm xuất hiện ở nhiều vị trí trong quá trình tháo gỡ. –

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