2012-03-09 60 views
5

Nếu lớp không có hàm tạo, trình biên dịch có tạo một hàm tạo mặc định cho nó không?Có đúng là một hàm tạo mặc định được tổng hợp cho mọi lớp không xác định một lớp không?

Các lập trình viên mới đến C++ thường có hai hiểu lầm phổ biến:

Đó một constructor mặc định được tổng hợp cho mỗi lớp mà không xác định một

từ cuốn sách Bên trong C++ Object Model

Tôi đang thua lỗ ...

+0

Truy vấn chính xác là gì? – iammilind

+0

AFAIK mọi lớp đều có hàm tạo mặc định, tuy nhiên tốt nhất bạn nên tạo nó (ít nhất là tôi làm nó). –

+0

Và câu hỏi là ...? – PlasmaHH

Trả lời

12

Điều này được giải thích rõ trong phần mà từ đó trích dẫn này được thực hiện. Tôi sẽ không diễn giải nó trọn vẹn, nhưng đây là một bản tóm tắt ngắn về nội dung của phần.

Trước hết, bạn cần phải hiểu các điều khoản sau: implicitly-declared, implicitly-defined, trivial, non-trivialsynthesized (một thuật ngữ được sử dụng bởi Stanley Lippman, nhưng không được sử dụng trong tiêu chuẩn).

ngầm-tuyên bố

Một constructor là implicitly-declared cho một lớp học nếu không có user-declared constructor trong lớp này. Ví dụ, lớp này struct T { }; không khai báo bất kỳ hàm tạo nào, do đó trình biên dịch ngầm khai báo một hàm tạo mặc định. Mặt khác, lớp này struct T { T(int); }; khai báo một hàm tạo, do đó trình biên dịch sẽ không khai báo một hàm tạo ngầm mặc định. Bạn sẽ không thể tạo một thể hiện của T mà không có tham số, trừ khi bạn xác định hàm tạo mặc định của riêng mình.

ngầm xác định

Một constructor implicitly-declaredimplicitly-defined khi nó được sử dụng, ví dụ: khi một thể hiện được tạo ra không có tham số. Giả sử lớp sau struct T { };, dòng T t; sẽ kích hoạt định nghĩa của T::T(). Nếu không, bạn sẽ có một lỗi linker vì constructor sẽ được khai báo nhưng không được định nghĩa. Tuy nhiên, một hàm tạo ngầm được xác định không nhất thiết phải có bất kỳ mã nào được liên kết với nó! Một hàm tạo mặc định là được tổng hợp (có nghĩa là một số mã được tạo cho nó) bởi trình biên dịch chỉ trong một số trường hợp nhất định.

constructor tầm thường

Một constructor implicitly-declared mặc định là trivial khi:

  • lớp của nó không có chức năng ảo và không có lớp cơ sở ảo và
  • lớp cơ sở của nó có trivial nhà thầu và
  • tất cả thành viên không tĩnh của nó có trivial nhà thầu.

Trong trường hợp này, trình biên dịch mặc định không có gì để làm, vì vậy không có mã nào được tổng hợp cho nó. Ví dụ, trong đoạn mã sau

struct Trivial 
{ 
    int i; 
    char * pc; 
}; 

int main() 
{ 
    Trivial t; 
} 

việc xây dựng t không liên quan đến bất kỳ hoạt động (bạn có thể thấy rằng bằng cách nhìn vào lắp ráp tạo ra: không có constructor được gọi là xây dựng t).

không tầm thường

Mặt khác, nếu lớp không đáp ứng được ba yêu cầu đã nêu ở trên, constructor implicitly-declared mặc định của nó sẽ non-trivial, có nghĩa là nó sẽ liên quan đến một số hoạt động mà phải được thực hiện để tôn trọng ngữ nghĩa ngôn ngữ. Trong trường hợp này, trình biên dịch sẽ synthesize thực hiện hàm tạo thực hiện các thao tác này.

Ví dụ, hãy xem xét các lớp sau đây:

struct NonTrivial 
{ 
    virtual void foo(); 
}; 

Kể từ khi nó có một hàm thành viên ảo, constructor mặc định của nó phải đặt con trỏ bảng ảo với giá trị đúng (giả sử việc thực hiện sử dụng một phương pháp ảo bàn, tất nhiên).

Tương tự như vậy, các nhà xây dựng của lớp này

struct NonTrivial 
{ 
    std::string s; 
}; 

phải gọi chuỗi constructor mặc định, vì nó không phải là trivial. Để thực hiện các hoạt động này, trình biên dịch tạo mã cho hàm dựng mặc định và gọi nó bất cứ lúc nào bạn tạo một cá thể không có tham số. Bạn có thể kiểm tra điều này bằng cách nhìn vào assembly tương ứng với instantiation này NonTrivial n; (bạn sẽ thấy một hàm call, trừ khi constructor đã được inlined).


Tóm tắt

Khi bạn không cung cấp bất kỳ constructor cho lớp học của bạn, trình biên dịch ngầm tuyên bố vỡ nợ một. Nếu bạn cố gắng sử dụng nó, trình biên dịch ngầm định nghĩa nó, nếu nó có thể (nó không phải lúc nào cũng có thể, ví dụ khi một lớp có một thành viên không cấu hình mặc định). Tuy nhiên, định nghĩa ngầm định này không hàm ý việc tạo ra bất kỳ mã nào. Trình biên dịch cần tạo mã cho hàm tạo (tổng hợp nó) chỉ khi nó không nhỏ, có nghĩa là nó liên quan đến các hoạt động nhất định cần thiết để thực hiện ngữ nghĩa ngôn ngữ.


N.B.

"Mô hình đối tượng bên trong C++ của Stanley B Lippman" và câu trả lời này đề cập đến (có thể) việc triển khai C++ chứ không phải ngữ nghĩa của nó. Kết quả là, không có điều nào ở trên có thể được tổng quát hóa cho tất cả các trình biên dịch: theo như tôi biết, việc triển khai thực hiện hoàn toàn được phép tạo mã ngay cả đối với một hàm tạo tầm thường. Từ quan điểm của người dùng C++, tất cả những gì quan trọng là khía cạnh "được khai báo rõ ràng/được định nghĩa" (và cũng là sự khác biệt tầm thường/không tầm thường, vì nó có một số hàm ý (ví dụ, một đối tượng của một lớp không tầm thường) nhà xây dựng không thể là một thành viên của một công đoàn))

+0

+1 - Thú vị Tôi cần xem qua cuốn sách này – birryree

+0

chờ đợi, nghĩa là tổng hợp có nghĩa là gì? –

+0

Tóm tắt không chính xác Có một số điều kiện khác về khả năng sẵn có của một hàm dựng mặc định hơn là "không có người xây dựng được khai báo bởi người dùng" –

0

Có một hàm tạo mặc định là al theo mặc định nếu bạn không định nghĩa một hàm tạo của riêng bạn (xem phần constructor mặc định here).

+0

Không, cuốn sách nói rằng đó là những hiểu lầm mới ' –

+0

Nếu anh ta định nghĩa một hàm tạo khác, hàm tạo mặc định không được tổng hợp. – juanchopanza

+0

@juanchopanza mmh yeah đó là lý do tại sao tôi viết "nếu bạn không định nghĩa" ... – talnicolas

4

Một constructor mặc định được tổng hợp cho mỗi lớp mà không xác định một nếu:

  • Mã sử ​​dụng các lớp cần một & chỉ khi
  • Không có nhà xây dựng khác được xác định một cách rõ ràng cho lớp của bạn .
+0

khi người dùng cần? Một ví dụ? lớp A {} int main() {A a; return 0;} –

+0

yep. 'A a;' yêu cầu một ctor mặc định. Vì vậy, một là generatoed. –

+0

Các quy tắc khá nghiêm ngặt ở đây. 'if (false) {A a; } 'cũng yêu cầu một ctor mặc định. Trình biên dịch không nhìn vào ngữ cảnh mà 'a' được định nghĩa. – MSalters

0

http://www.codeguru.com/forum/archive/index.php/t-257648.html

Trích:

Các sentense Sau đây là nhận được từ cuốn sách "Bên trong C++ mô hình đối tượng", được viết bởi Stanley B. Lippman.

Có bốn đặc điểm của một lớp, theo đó trình biên dịch cần để tổng hợp một constructor mặc định cho các lớp thực hiện kê khai không constructor ở tất cả. Tiêu chuẩn đề cập đến những điều này là tiềm ẩn, các nhà xây dựng mặc định không độc quyền. Trình xây dựng được tổng hợp chỉ đáp ứng chỉ cần thực hiện. Nó thực hiện điều này bằng cách gọi đối tượng thành viên hoặc các hàm tạo mặc định của lớp cơ sở hoặc khởi tạo hàm ảo hoặc cơ chế lớp cơ sở ảo cho từng đối tượng. Các lớp không thể hiện các đặc điểm này và tuyên bố không có hàm tạo nào ở tất cả các số được cho là có các hàm tạo ngầm mặc định, tầm thường. Trong thực tế, các hàm tạo mặc định tầm thường này không được tổng hợp. ... Các lập trình viên mới đến C++ thường có hai hiểu lầm phổ biến:

  • Đó một constructor mặc định được tổng hợp cho mỗi lớp mà không xác định một

  • Đó là constructor mặc định trình biên dịch tổng hợp cung cấp rõ ràng trình khởi tạo mặc định cho mỗi thành viên dữ liệu được khai báo trong lớp

Như bạn đã thấy, không phải điều này là đúng.

+0

Vui lòng không trả lời bằng các URL ngẫu nhiên. Câu trả lời hay có thể trỏ đến tài nguyên "để biết thêm thông tin", nhưng phải tự đứng vững. – MSalters

9

Tôi nghĩ rằng quan niệm sai lầm là:

Đó một constructor mặc định được tổng hợp cho mỗi lớp mà không xác định một

Đó mọi người nghĩ các nhà xây dựng mặc định, mà chấp nhận không có đối số, sẽ luôn được tạo nếu bạn không tự khai báo.

Tuy nhiên, điều này không đúng, bởi vì nếu bạn tự khai báo bất kỳ hàm tạo nào, trình mặc định sẽ không được tạo tự động.

class MyClass { 
public: 
    MyClass(int x) {}; // No default constructor will be generated now 
}; 

Điều này sẽ dẫn đến các vấn đề như khi mới bắt đầu mong đợi để sử dụng MyClass như thế này:

MyClass mc; 

Mà sẽ không hoạt động vì không có constructor mặc định mà chấp nhận không args.

chỉnh sửa vì OP vẫn còn một chút nhầm lẫn.

Hãy tưởng tượng rằng MyClass của tôi ở trên là thế này:

class MyClass { 
}; 

int main() { 
    MyClass m; 
} 

Điều đó sẽ biên dịch, bởi vì trình biên dịch sẽ autogenerate constructor mặc định MyClass()MyClass được sử dụng.

Bây giờ hãy xem này:

#include <iostream> 

class MyClass { 

}; 

int main() { 
    std::cout << "exiting\n"; 
} 

Nếu đây là mã duy nhất xung quanh, trình biên dịch sẽ thậm chí không bận tâm tạo ra các constructor mặc định, vì MyClass không bao giờ được sử dụng.

Bây giờ này:

#include <iostream> 

class MyClass { 
public: 
    MyClass(int x = 5) { _x = x; } 
    int _x; 
}; 

int main() { 
    MyClass m; 
    std::cout << m._x; 
} 

Trình biên dịch không tạo ra constructor mặc định MyClass(), bởi vì lớp đã có một constructor được định nghĩa bởi tôi. Điều này sẽ làm việc và MyClass(int x = 5) hoạt động như hàm tạo mặc định của bạn vì nó có thể chấp nhận không có đối số, nhưng nó không được trình biên dịch tạo ra.

Và cuối cùng, nơi người mới bắt đầu có thể chạy vào một vấn đề:

class MyClass() { 
public: 
    MyClass(int x) { _x = x; } 
    int _x; 
}; 

int main() { 
    MyClass m; 
} 

trên sẽ ném bạn một lỗi trong quá trình biên soạn, vì MyClass m cần một constructor mặc định (không có đối số) để làm việc, nhưng bạn đã tuyên bố tình hàm tạo phải mất một số int. Trình biên dịch sẽ không tạo ra một hàm tạo không có đối số trong tình huống này.

+0

bạn xác định một ... –

+2

@ user974349 - Trình xây dựng mặc định trong C++ có định nghĩa rất rõ ràng: đó là hàm tạo không chấp nhận đối số (hoặc tất cả đối số có đối số mặc định). – birryree

+1

Không được nhầm lẫn với "hàm dựng mặc định mặc định", là hàm khởi tạo mặc định được xác định ngầm định hoặc hàm khởi tạo mặc định được mặc định rõ ràng với '= mặc định" –

3

Tất cả các câu trả lời upvoted do đó dường như xa để nói xấp xỉ cùng một điều:.

một constructor mặc định được tổng hợp cho mỗi lớp mà không có bất kỳ người sử dụng - hàm tạo được định nghĩa.

là odification của tuyên bố trong câu hỏi, có nghĩa là

Một constructor mặc định được tổng hợp cho mỗi lớp mà không có một người dùng định nghĩa constructor mặc định.

Sự khác biệt là quan trọng, nhưng tuyên bố vẫn sai.

Một tuyên bố chính xác sẽ là:

Một constructor mặc định được tổng hợp cho mỗi lớp mà không có bất kỳ người dùng định nghĩa constructor và mà tất cả các tiểu đối tượng là mặc định-constructible trong bối cảnh của lớp.

Dưới đây là một số phản ví dụ rõ ràng để báo cáo kết quả đầu tiên:

struct NoDefaultConstructor 
{ 
    NoDefaultConstructor(int); 
}; 

class Surprise1 
{ 
    NoDefaultConstructor m; 
} s1; // fails, no default constructor exists for Surprise1 

class Surprise1 không có nhà xây dựng người dùng định nghĩa, nhưng không có constructor mặc định được tổng hợp.

Nó không quan trọng cho dù subobject là thành viên hoặc cơ sở:

class Surprise2 : public NoDefaultConstructor 
{ 
} s2; // fails, no default constructor exists for Surprise2 

Ngay cả khi tất cả subobjects là mặc định-constructible, các nhà xây dựng mặc định có để có thể truy cập từ lớp composite:

class NonPublicConstructor 
{ 
protected: 
    NonPublicConstructor(); 
}; 

class Surprise3 
{ 
    NonPublicConstructor m; 
} s3; // fails, no default constructor exists for Surprise3 
+0

Tôi không nghĩ rằng điều này là chính xác. 'Surprise1' ** không ** có một hàm tạo ngầm được xác định, nhưng hàm tạo này cố gắng gọi' NoDefaultConstructor :: NoDefaultConstructor() ', không tồn tại. Hàm dựng mặc định tổng hợp gọi một hàm không tồn tại, do đó lỗi trình biên dịch. Trong Comeau, khi bạn biên dịch mẫu của bạn, thông báo lỗi nói rằng 'không có hàm tạo mặc định nào tồn tại cho lớp" NoDefaultConstructor "', không phải là 'không có hàm tạo mặc định nào tồn tại cho lớp" Ngạc nhiên "'. –

+0

Đầu ra GCC 'không có hàm kết hợp nào để gọi đến‘ NoDefaultConstructor :: NoDefaultConstructor() ’'. Tôi nghĩ rằng hai trình biên dịch này có đúng, vì tiêu chuẩn này khá rõ ràng về điểm này: 'Nếu không có hàm tạo do người dùng khai báo cho lớp X, thì hàm tạo mặc định được khai báo hoàn toàn.' (§12.1.5) Trình biên dịch đã làm gì bạn sử dụng để kiểm tra mẫu của bạn? –

+0

@Luc: Trình biên dịch đang cố gắng tổng hợp 'Surprise1 :: Surprise1' và không thực hiện được. Để chỉ nói "trình biên dịch tổng hợp nó" ngụ ý thành công. Ngoài ra, tiêu chuẩn nói rằng trong trường hợp như vậy, hàm tạo mặc định được ngầm định nghĩa là * deleted *. Nó không tồn tại. –

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