2009-06-22 8 views
16

tôi có ba lớp cơ sở khác nhau:hàm C++ trọng

class BaseA 
{ 
public: 
    virtual int foo() = 0; 
}; 

class BaseB 
{ 
public: 
    virtual int foo() { return 42; } 
}; 

class BaseC 
{ 
public: 
    int foo() { return 42; } 
}; 

sau đó tôi lấy được từ cơ sở như thế này (thay thế X cho A, B hoặc C):

class Child : public BaseX 
{ 
public: 
    int foo() { return 42; } 
}; 

thế nào là chức năng ghi đè trong ba lớp cơ sở khác nhau? Ba giả định sau đây của tôi có đúng không? Có bất kỳ cảnh báo nào khác không?

  • Với BaseA, lớp con không biên dịch, hàm ảo thuần túy không được xác định.
  • Với BaseB, chức năng trong trẻ được gọi khi gọi foo trên BaseB * hoặc Child *.
  • Với BaseC, chức năng trong trẻ được gọi khi gọi foo trên Child * nhưng không phải trên BaseB * (chức năng trong lớp cha được gọi).

Trả lời

13

Trong lớp dẫn xuất, một phương thức là ảo nếu nó được định nghĩa ảo trong lớp cơ sở, ngay cả khi từ khóa ảo không được sử dụng trong phương thức của lớp dẫn xuất.

  • Với BaseA, nó sẽ biên dịch và thực hiện như dự định, với foo() là ảo và thực hiện trong lớp Child.
  • Tương tự với BaseB, nó cũng sẽ biên dịch và thực hiện như dự định, với foo() là virtual() và thực hiện trong lớp Child.
  • Với BaseC tuy nhiên, nó sẽ biên dịch và thực thi, nhưng nó sẽ thực thi phiên bản BaseC nếu bạn gọi nó từ ngữ cảnh BaseC và phiên bản Child nếu bạn gọi với ngữ cảnh Child.
16

Quy tắc quan trọng cần nhớ là một khi hàm được khai báo ảo, hàm có chữ ký trùng khớp trong lớp dẫn xuất luôn là ảo. Vì vậy, nó được ghi đè cho Child of A và Child of B, sẽ hoạt động giống hệt nhau (ngoại trừ bạn không thể khởi tạo trực tiếp BaseA).

Với C, tuy nhiên, chức năng không bị ghi đè nhưng bị quá tải. Trong tình huống đó, chỉ có kiểu tĩnh quan trọng: nó sẽ gọi nó là con trỏ tới (kiểu tĩnh) thay vì đối tượng thực sự là gì (kiểu động)

0

Lớp con sẽ biên dịch nếu được lấy từ A, bạn không thể khởi tạo các đối tượng thuộc loại đó.

Điều này có thể có giá trị nếu bạn định ghi đè một số chức năng từ Cơ sở và sau đó lấy lại.

2

Với BaseA, lớp trẻ không biên dịch, tinh khiết chức năng ảo không được định nghĩa

Điều này đúng chỉ khi bạn cố gắng để tạo ra một đối tượng của BaseA.Nếu bạn tạo một đối tượng của trẻ em và sau đó bạn có thể gọi foo() sử dụng một trong hai BaseA * hoặc Child *

Với BaseB, chức năng ở trẻ em được gọi khi gọi foo trên BaseB * hoặc trẻ em * .

Phụ thuộc vào loại đối tượng làm đối tượng có thể là BaseB hoặc Trẻ em. Nếu đối tượng là BaseB thì BaseB :: foo được gọi.

Với BaseC, thefunction trong con được gọi khi gọi foo trên Child * nhưng không phải trên BaseB * (các chức năng trong lớp cha được gọi).

Có, nhưng bạn không bao giờ muốn thực hiện việc này.

+0

Có lý do cụ thể nào khiến tôi không muốn làm điều này không? Mặc dù tôi không thể, ngược lại, đưa ra một ví dụ mà tôi muốn * để làm điều đó. Có lý do kỹ thuật hay nó chỉ được coi là "thực hành xấu"? – Mizipzor

+1

Tôi không thấy bất kỳ lý do kỹ thuật nào .. nhưng nó thực sự khó hiểu. – Naveen

+0

@mizipzor Có thể bạn đang thừa hưởng các lớp của một số người khác, nhưng bạn không có mã của họ. Không phải là một thiết kế lý tưởng nhưng nó có thể xảy ra. – Kae

2

Từ quan điểm đa hình, thích A, vì vậy bạn biết rằng mỗi đứa trẻ đều có chức năng ảo của riêng mình.
Chọn B chủ yếu nếu bạn có triển khai mặc định hợp lệ, nhưng sau đó bạn phải đảm bảo rằng tất cả các lớp con đều có triển khai của riêng chúng khi cần. C không phải là đa hình, vì vậy hãy sử dụng một cách thận trọng.

1

Nó chủ yếu phụ thuộc vào cách bạn gọi nó.

nếu bạn đã làm:

class Child : public BaseA 
{ 
public: 
    int foo() { return 42; } 
}; 

và đã

BaseA baseA = new Child(); 
baseA->foo(); 

Nó sẽ gọi hàm foo Trẻ.

Tuy nhiên, nếu bạn đã làm điều này:

BaseA baseA = new BaseA(); 

Nó sẽ tạo ra một lỗi thời gian biên dịch.