2011-08-26 37 views
13

Các tiêu chuẩn C++ 98/C++ 03 là gì và các quy tắc chính xác trong tương lai của C++ 0x cho dominance in virtual inheritance?Sự thống trị trong thừa kế ảo

Tôi không yêu cầu chỉ các đoạn cụ thể, mặc dù tôi cũng yêu cầu điều đó (ở đâu đó trong phần 10, tôi đoán).

Tôi cũng yêu cầu về hậu quả của tiêu chuẩn, tiêu chuẩn được giải thích rõ ràng.

+0

Bạn có ý nghĩa gì khi "thống trị" liên quan đến thừa kế ảo? –

+1

@Nicol: chỉ mục tiêu chuẩn C++ 98 đề cập đến thừa kế ảo và trang 167. đó là tất cả các tiêu chuẩn nói trực tiếp (và theo như tôi biết chỉ mục là không quy chuẩn). giải thích sự thống trị sẽ mất nhiều hơn bình luận này, vì vậy, chỉ cần google nó, hoặc [wikipedia] (http://en.wikipedia.org/wiki/Dominance_%28C%2B%2B%29) - nếu bạn không biết về nó, bạn có thể không trả lời được câu hỏi này. chúc mừng, –

+0

@Nicol: cảm ơn vì đã hỏi, tôi đã thêm liên kết vào câu hỏi. –

Trả lời

25

Tôi nghĩ rằng đây là ngôn ngữ bạn đang tìm kiếm. Trong C++ 03 ISO spec, trong § 10,2/2, chúng tôi có những điều sau đây:

Các bước sau đây xác định kết quả của tra cứu tên trong một phạm vi lớp, C. Đầu tiên, mỗi lời tuyên bố cho tên trong lớp và trong mỗi đối tượng phụ lớp cơ sở của nó được xem xét. Tên thành viên f trong một đối tượng con B ẩn một tên thành viên f trong một đối tượng con A nếu A là đối tượng phụ lớp cơ sở của B. Mọi khai báo bị ẩn sẽ bị loại trừ khỏi việc xem xét. Mỗi khai báo được giới thiệu bởi một khai báo sử dụng được coi là từ mỗi đối tượng con của C, đó là kiểu khai báo được chỉ định bởi khai báo sử dụng. Nếu tập hợp kết quả khai báo không phải là tất cả từ các đối tượng phụ cùng loại hoặc tập hợp có thành viên không phải là thành viên và bao gồm các thành viên từ các đối tượng con riêng biệt, có một sự mơ hồ và chương trình không đúng định dạng. Nếu không, đó là kết quả của việc tra cứu.

Ở mức cao, điều này có nghĩa là khi bạn tìm kiếm tên, nó sẽ tìm tất cả các lớp cơ sở và chính lớp đó để tìm các khai báo cho tên đó. Sau đó bạn đi từng lớp và nếu một trong những đối tượng cơ sở đó có thứ gì đó với tên đó, nó sẽ ẩn tất cả các tên được giới thiệu trong bất kỳ lớp cơ sở của đối tượng đó.

Một chi tiết quan trọng ở đây là dòng này:

Bất kỳ tờ khai được để ẩn được loại bỏ từ xem xét.

Quan trọng, điều này nói rằng nếu nội dung nào đó bị ẩn bởi bất kỳ điều gì, nó được coi là bị ẩn và bị xóa. Vì vậy, ví dụ: nếu tôi làm điều này:

      class D { 
          public: 
           void f(); 
          } 

    class B: virtual public D {  class C: virtual public D { 
    public:       public: 
     void f();       /* empty */ 
    };         }; 

         class A: public B, public C { 
         public: 
          void doSomething() { 
           f(); // <--- This line 
          } 
         }; 

Trên dòng được chỉ định, cuộc gọi đến f() được giải quyết như sau. Trước tiên, chúng tôi thêm B::fD::f vào nhóm tên có thể được xem xét. D::f không ẩn bất kỳ thứ gì vì D không có lớp cơ sở. Tuy nhiên, B::f không ẩn D::f, vì vậy mặc dù D::f có thể truy cập từ A mà không nhìn thấy B::f, nó được coi là ẩn và xóa khỏi tập hợp các đối tượng có thể được đặt tên là f. Vì chỉ còn lại B::f, đó là cái được gọi. Spec ISO đề cập (§ 10,2/7) mà

Khi lớp cơ sở ảo được sử dụng, một tuyên bố ẩn có thể đạt được cùng một con đường thông qua các sub-object mạng mà không đi qua các tuyên bố lẩn trốn. Đây không phải là một sự mơ hồ. [...]

Tôi nghĩ rằng điều này là do quy tắc trên.

Trong C++ 11 (theo dự thảo spec N3242), các quy tắc được viết rõ ràng hơn nhiều so với trước đây và một thuật toán thực tế được đưa ra để tính toán tên có ý nghĩa gì. Đây là ngôn ngữ, từng bước một.

Chúng tôi bắt đầu với § 10,2/3:

Các tra cứu thiết lập cho f trong C, gọi là S (f, C), bao gồm hai bộ linh kiện: khai báo thiết, một tập hợp của các thành viên có tên là f; và bộ phụ đề được thiết lập, một tập hợp các đối tượng con có thể khai báo các thành viên này (có thể bao gồm cả khai báo sử dụng). Trong bộ khai báo, các khai báo sử dụng được thay thế bởi các thành viên mà họ chỉ định và khai báo kiểu (bao gồm cả các tên lớp được tiêm) được thay thế bằng các kiểu mà chúng chỉ định. S (f, C) được tính như sau:

Trong ngữ cảnh này, C là phạm vi mà tra cứu xảy ra. Trong các từ khác, tập hợp S(f, C) có nghĩa là "những khai báo nào có thể nhìn thấy khi tôi cố tìm kiếm f trong phạm vi lớp học C?" Để trả lời điều này, spec xác định một thuật toán để xác định điều này. Bước đầu tiên là như sau: (§ 10,2/4)

Nếu C chứa một tuyên bố của tên f, tập khai chứa tất cả các tuyên bố của f khai báo trong C thỏa mãn các yêu cầu của ngôn ngữ xây dựng trong mà tra cứu xảy ra. [...] Nếu bộ khai báo kết quả không trống, bộ phụ đề chứa C và tính toán hoàn tất.

Nói cách khác, nếu các lớp chính nó có một cái gì đó gọi là f khai báo trong nó, sau đó thiết lập tuyên bố chỉ là tập hợp của những tên f định nghĩa trong lớp đó (hoặc nhập khẩu với một tuyên bố using). Tuy nhiên, nếu chúng tôi không thể tìm thấy bất kỳ thứ gì có tên là f hoặc nếu mọi thứ có tên là f là sai phân loại (ví dụ: khai báo hàm khi chúng tôi muốn một loại), thì chúng tôi tiếp tục sang bước tiếp theo: (§ 10.2/5)

Nếu không (nghĩa là C không chứa khai báo f hoặc bộ khai báo kết quả trống), S (f, C) ban đầu trống. Nếu C có các lớp cơ sở, hãy tính toán tập hợp tra cứu cho f trong mỗi lớp con cơ sở trực tiếp B i và hợp nhất mỗi bộ tra cứu S (f, B i) thành S (f, C).

Nói cách khác, chúng ta sẽ xem xét các lớp cơ sở, tính toán tên có thể đề cập đến trong các lớp cơ sở đó, sau đó hợp nhất mọi thứ lại với nhau. Cách thực tế mà bạn thực hiện việc hợp nhất được xác định trong bước tiếp theo. Điều này thực sự phức tạp (nó có ba phần với nó), do đó, đây là blow-by-blow. Đây là từ ngữ gốc: (§ 10.2/6)

Các bước sau đây xác định kết quả của việc sáp nhập tra cứu tập S (f, B i) vào chỉ số S trung gian (f, C):

  • Nếu mỗi người trong số các các thành viên phụ của S (f, B i) là một lớp con cơ sở của ít nhất một trong số các thành phần con là S2 (0, 35) f, C) không thay đổi và quá trình hợp nhất hoàn tất. Ngược lại, nếu mỗi thành viên subobject của S (f, C) là một subobject lớp cơ sở của ít nhất một trong số thành viên subobject của S (f, B ), hoặc nếu S (f, C) rỗng , S (f, C) mới là bản sao của S (f, Bi).

  • Ngược lại, nếu các bộ tuyên bố của S (f, B i) và S (f, C) khác nhau, việc hợp nhất là mơ hồ: các S mới (f, C) là một tra cứu thiết lập với một bộ khai báo không hợp lệ và sự kết hợp của các tập hợp con. Trong các lần hợp nhất sau , một bộ khai báo không hợp lệ được coi là khác với bất kỳ tập hợp nào khác.

  • Nếu không, S mới (f, C) là bộ tra cứu có bộ khai báo chung và liên kết của bộ phụ đề .

Được rồi, chúng ta hãy bỏ từng cái này cùng một lúc. Quy tắc đầu tiên ở đây có hai phần. Phần đầu tiên nói rằng nếu bạn đang cố gắng hợp nhất một tập hợp các tờ khai trống vào tập hợp tổng thể, bạn không làm gì cả. Điều đó có ý nghĩa. Nó cũng nói rằng nếu bạn đang cố gắng để kết hợp một cái gì đó trong đó là một lớp cơ sở của tất cả mọi thứ đã được sáp nhập cho đến nay, sau đó bạn không làm bất cứ điều gì cả. Điều này là quan trọng, bởi vì nó có nghĩa là nếu bạn đã giấu một cái gì đó, bạn không muốn vô tình giới thiệu lại nó bằng cách hợp nhất nó lại.

Phần thứ hai của quy tắc đầu tiên nói rằng nếu điều bạn đang hợp nhất trong có nguồn gốc từ tất cả mọi thứ đã được sáp nhập cho đến nay, bạn thay thế các thiết lập bạn đã tính toán đến thời điểm này với các dữ liệu bạn đã tính cho loại có nguồn gốc. Về cơ bản, nếu bạn đã hợp nhất với nhau nhiều lớp dường như chưa được kết nối và sau đó hợp nhất trong một lớp hợp nhất tất cả chúng, hãy loại bỏ dữ liệu cũ và chỉ sử dụng dữ liệu cho loại có nguồn gốc đó, mà bạn đã tính .

Bây giờ, hãy đi đến quy tắc thứ hai đó. Điều này đã cho tôi một thời gian để hiểu, vì vậy tôi có thể có điều này sai, nhưng tôi nghĩ rằng nó nói rằng nếu bạn tiến hành tra cứu trong hai lớp cơ sở khác nhau và lấy lại những thứ khác nhau, sau đó tên là mơ hồ và bạn nên báo cáo rằng một cái gì đó nếu bạn cố gắng tra cứu tên tại thời điểm này.

Quy tắc cuối cùng nói rằng nếu chúng tôi không thuộc một trong các trường hợp đặc biệt này, không có gì sai và bạn chỉ nên kết hợp chúng.

Phew ... thật khó! Hãy xem điều gì sẽ xảy ra khi chúng ta theo dõi điều này cho sự thừa kế kim cương ở trên. Chúng tôi muốn tìm kiếm tên f bắt đầu từ A. Vì A không xác định f, chúng tôi tính giá trị của việc tra cứu f bắt đầu từ Bf bắt đầu từ C. Hãy xem điều gì xảy ra. Khi tính toán giá trị của những gì f có nghĩa là trong B, chúng tôi thấy rằng B::f được xác định và do đó chúng tôi ngừng tìm kiếm.Giá trị của nhìn lên f trong B là tập hợp (B::f, B}. Để tìm kiếm những gì f nghĩa trong C, chúng ta nhìn vào C và thấy rằng nó không định nghĩa f, vì vậy chúng tôi một lần nữa đệ quy tìm kiếm các giá trị từ D. Thực hiện tra cứu trong D sản lượng {D::f, D} và khi chúng tôi hợp nhất mọi thứ với nhau, chúng tôi thấy rằng nửa thứ hai của quy tắc 1 được áp dụng (vì nó không đúng là mọi đối tượng trong tập con là cơ sở của D) giá trị cho C được cho bởi {D::f, D}.

F trong nội bộ, chúng tôi cần hợp nhất các giá trị cho BC. Điều này cố gắng hợp nhất {D::f, D} và {B::f, B}. Đây là nơi nó được vui vẻ. Giả sử chúng ta hợp nhất theo thứ tự này. Hợp nhất {D::f, D} và bộ trống sản xuất {D::f, D}. Khi chúng tôi hợp nhất vào {B::f, B}, vì D là cơ sở của B, vào nửa sau của quy tắc một, chúng tôi ghi đè tập hợp cũ của chúng tôi và kết thúc với {B::f, B}. Do đó, tra cứu f là phiên bản f trong B.

Nếu, mặt khác, chúng ta hợp nhất theo thứ tự ngược lại, chúng ta bắt đầu với {B::f, B} và cố gắng sáp nhập trong {D::f, D}. Nhưng vì D là cơ sở của B, chúng tôi chỉ bỏ qua nó, để lại {B::f, B}. Chúng tôi đã đạt được kết quả tương tự. Khá tuyệt, huh? Tôi ngạc nhiên rằng điều này hoạt động rất tốt!

Vì vậy, có bạn có nó - các quy tắc cũ thực sự là (ish) đơn giản, và các quy tắc mới là không thể phức tạp nhưng bằng cách nào đó quản lý để làm việc ra anyway.

Hy vọng điều này sẽ hữu ích!

+0

@Alf P. Steinbach- Tôi vừa mới cập nhật điều này với các quy tắc cho C++ 11 và người đàn ông thật là điên rồ. Hy vọng điều này sẽ giúp giải thích mọi thứ (?)! – templatetypedef

+0

+1 để lội vào đây. Tôi nghĩ rằng tôi hiểu phần C++ 98 bây giờ. ĐƯỢC. Đáng ngạc nhiên đơn giản. Tuy nhiên, mắt tôi lóe lên khi tôi bắt đầu đọc phần C++ 11 ... Chúc mừng, –

+2

+1 chỉ vì những nỗ lực phải đi vào câu trả lời này o_O – ildjarn