2013-09-06 54 views
5

Tôi đang thử nghiệm mã này và tự hỏi tại sao điều này không thành công tại thời gian biên dịch? Tôi đang sử dụng C++ 11 và g ++ 4.7.2.Hành vi lạ thường của hàm tạo C++

Tôi có cấu trúc tương tự trên mã sản xuất của mình, nó đã cho lỗi trong thời gian chạy, sau đó tôi thấy rằng tôi đang xây dựng lớp với kiểu đối số sai.

#include <iostream> 
#include <vector> 


typedef std::vector<std::string> Word; 

class Data { 
    public: 
     const Word &word; 
     Data(Word w) : word(w) {} 
}; 

class Base{ 
    const Data &data; 
    public: 
     Base(const Data &d): data(d) {} 
     ~Base() {} 
}; 

class Work : public Base{ 
    public: 
     Work(const Data &d): Base(d){} 
     ~Work() {} 
}; 


int main(int argc, char **argv){ 
    Word words; 
    words.push_back("work"); 

    /* 
    * I'm confused with this constructor, why this passed the compilation 
    * ?? 
    * Any special rule to reason this scenario ?? 
    * 
    * But obviously it will fail at run time. 
    */ 
    const Work *work = new Work(words); 

    return 0; 
} 
+1

Có thể một chuyển đổi tiềm ẩn đang diễn ra ở đâu đó? –

+0

Các từ được chuyển đổi thành Dữ liệu theo phương thức dựng dữ liệu và sau đó gọi Công việc (Dữ liệu) – ZijingWu

+0

Tại sao từ đó không thành công? Trên http://ideone.com/cxkf4X nó trả về thành công. – xanatos

Trả lời

10

Data là constructible từ Word, vì vậy bạn có thể vượt qua một Word để các nhà xây dựng Work. Dưới mui xe, một thể hiện của Data sẽ được tạo ra từ số Word được chuyển và được chuyển cho hàm tạo.

Bạn có thể tránh điều này bằng cách đánh dấu các nhà xây dựng của Data mà phải mất một Word như explicit, như thế này:

class Data { 
    public: 
     const Word &word; 
     explicit Data(Word w) : word(w) {} 
}; 

Bằng cách đó, các nhà xây dựng không thể được mặc nhiên áp dụng nữa, và cuộc gọi của bạn để các nhà xây dựng Work sẽ thất bại trong việc biên dịch, trừ khi bạn gọi một cách rõ ràng Data constructor:

const Work *work = new Work(words);  // Implicit call, fails to compile. 
const Work *work = new Work(Data(words)); // Explicit call, compiles. 
+1

Tất nhiên, hàm tạo của 'Dữ liệu' sẽ không hoạt động, bởi vì nó liên kết tham chiếu tới đối số, sẽ bị hủy khi hàm tạo trả về. –

+1

@MM., Vâng, đúng vậy. 'words' là local thành' main() 'không quan trọng nhiều vì instance của' Work' cũng là local thành 'main()', nhưng constructor 'Work' giữ một instance' Data' tạm thời, như James points và đó là tin xấu. –

+1

Bây giờ tôi hiểu lý do tại sao chương trình của tôi cho segfault trong khi chạy thời gian (treo khi truy cập vào tham chiếu lơ lửng). –

5

làm việc đồng mpiles *Data có một constructor chuyển đổi ngầm mà phải mất một tài liệu tham khảo Word:

Data(Word w) : word(w) {} 

Điều này có nghĩa bạn có thể làm những việc như

Word words; 
Data d1 = words; 
Data d2(words); 

và bạn có thể xây dựng một Work từ một Data qua constructor Work(const Data &d): Base(d){}:

Work w(d2); 

có nghĩa là sau đây cũng có giá trị, bởi vì nó chỉ liên quan đến một chuyển đổi người dùng định nghĩa:

Work w2(words); // constructs Data temporary from words, then constructs w2 with it 

Hành vi này có thể bị dập tắt bằng cách tuyên bố các nhà xây dựng chuyển đổi như explicit:

explicit Data(Word w) : word(w) {} 

* doesn của bạn 't thực sự làm việc vì nó liên quan đến một tham chiếu lơ lửng đối tượng tạm thời Data

+0

Bên cạnh câu hỏi, đó có phải là hành vi không xác định do tham chiếu lơ lửng trong lớp 'Dữ liệu' không? Từ 'đề cập đến một đối tượng tạm thời. – deepmax

+0

@MM. Tôi chỉ cần thêm một lưu ý về điều đó :) – juanchopanza

2

Sau khi thoát khỏi hàm tạo sau, dữ liệu thành viên word loại tài liệu tham khảo sẽ được tham khảo một đối tượng mà không còn tồn tại:

class Data { 
    public: 
     const Word &word; 
     Data(Word w) : word(w) {} 
}; 

Một biến thời hạn lưu trữ tự động được tạo ra để giữ w. Bạn lưu trữ một tham chiếu đến biến đó là một thành viên word, sau đó trọng tài bị phá hủy khi thoát khỏi hàm tạo.

Bất kể vấn đề nào khác, tôi không nghĩ đây là ý định của bạn.