2012-02-26 6 views
6

Các mã sau đây không biên dịch trong gcc:C++ và tiêm tên cơ sở

namespace One{ 
    class A{ 
    }; 
}; 

namespace Two{ 
    class A{ 
     public: 
     void what(){ 
      cout << "Two::A says what!" << endl; 
     } 
    }; 

    class B : public One::A{ 
     public: 
     B(){ 
      A xx; 
      xx.what(); 
     } 
    }; 

}; 

Và nó mang lại:

gccbug.cpp: In constructor ‘Two::B::B()’: 
gccbug.cpp:23: error: ‘class One::A’ has no member named ‘what’ 

Bây giờ, tôi đã nói rằng đây là hành vi đúng (do cơ sở tiêm name of One :: A make A tham chiếu đến One :: A). Tuy nhiên, mã này biên dịch trong C# (tốt, sau khi thay đổi một vài điều), do đó, điều này có vẻ là c + + cụ thể.

Điều tôi đang thắc mắc là .. tại sao? Có mục đích cụ thể nào để tiêm tên cơ sở "One :: A" là "A"?

+6

Chỉ để tham khảo, C#! = C++. Tôi không chắc chắn tại sao bạn so sánh hai số –

+0

Tôi không thể nói chắc chắn (hiện tại tôi không có bản sao của mình), nhưng ngoài ra, các câu hỏi về lý do tại sao ngôn ngữ C++ được thiết kế theo cách thường được trả lời trong một cuốn sách có tên "Thiết kế và tiến hóa của C++" của Stroustrup. –

+0

@TonyTheLion, Vâng, đó là ngôn ngữ lập trình giống c khác có "không gian tên" và một trình duyệt mà tôi có quyền truy cập vào trình biên dịch. Tôi không nói rằng chỉ có "Một cách thực sự" trong đó điều này là chính xác, nhưng điều này dường như không trực quan, ít nhất là đưa ra ví dụ này. – kamziro

Trả lời

3

Lý do duy nhất tôi có thể nghĩ đến là trong C++ bạn có khả năng để tham khảo các tên lớp cơ sở trong danh sách khởi tạo của các nhà xây dựng, như thế này:

namespace Two { 

    /*...*/ 

    class B : public One::A { 
    public: 
    B():A() 
    { 
     /*...*/ 
    } 
    }; 
} 

Dĩ nhiên mục đích sau đó là khác nhau từ ví dụ của bạn, bởi vì bạn thực sự khai báo biến cục bộ bên trong hàm tạo, trong khi trong ví dụ của tôi, A() đề cập đến đối tượng kiểu A ẩn trong định nghĩa của class B do thừa kế.

Tuy nhiên, tình huống của ví dụ của tôi có nhiều khả năng xảy ra, vì vậy tôi đoán họ nghĩ chúng ta không yêu cầu không gian tên phải được làm rõ ràng trong trường hợp này. Kết quả là, bất kỳ tham chiếu nào đến A mà không có vùng tên được hiểu là tham chiếu đến lớp cơ sở, thay vì bất kỳ lớp nào khác có tên là A, ngay cả khi nó nằm trong cùng một không gian tên như khai báo B.

+0

Ah, vâng, điều đó có ý nghĩa thực sự. Mặc dù tôi nói rằng nó cảm thấy một chút như một "hack". – kamziro

3

Có mục đích cụ thể nào để tiêm tên cơ sở "One :: A" là "A" không?

Có. Nó là như vậy mà bạn có thể viết này:

namespace N 
{ 
    class A 
    { 
     A *a; 
    }; 
} 

Trong vắng mặt của tiêm-tên, bạn đã viết N::A *a mà không phải là tốt đẹp.

Lưu ý rằng đó là vì tiêm-tên, những dòng sau được phép:

A::A *a1; //ok 
A::A::A *a2; //ok 
A::A::A::A *a3; //ok 
A::A::A::A::A *a4; //ok 
//and so on 

Online demo

+0

Điều này khác với tình huống do người hỏi giải thích?Trong ví dụ ban đầu 'lớp B' nằm bên trong một không gian tên, nhưng lớp mà nó bắt nguồn từ một không gian tên khác. – jogojapan

+1

@jogojapan Nhưng 'lớp B' kế thừa tất cả các tên từ lớp cơ sở của nó, bao gồm tên được tiêm của lớp cơ sở. Đang ở trong một phạm vi bên trong, nó giấu tên từ không gian tên xung quanh. –

+0

@Bo Persson Có. Tôi chỉ chỉ ra rằng ví dụ trên không hoàn toàn minh họa điểm này. Trong ví dụ trên, tham chiếu duy nhất tới 'lớp A' nằm trong cùng một không gian tên như khai báo của chính lớp đó. Bạn không cần tiêm tên cho điều đó. – jogojapan

0

By đủ điều kiện A với One:: bạn thêm A từ namespace một trong phạm vi, vì vậy trình biên dịch sẽ tìm ở đó cho độ phân giải tên của nó.

+0

Nhưng đối tượng của kiểu 'A' được khai báo bên trong hàm tạo đã không đủ điều kiện với' One :: '. Nó được hiểu là thuộc về 'namespace One', mặc dù việc khai báo' class B' chính nó nằm trong 'namespace Two'. – jogojapan