2010-04-09 37 views
38

Gần đây tôi đã biết rằng trong các chức năng ảo thuần túy C++ có thể tùy chọn có một phần thân.Trường hợp sử dụng các chức năng ảo thuần túy với cơ thể?

Trường hợp sử dụng trong thế giới thực cho các chức năng như vậy là gì?

+2

có vẻ giống như một sự lừa đảo của http://stackoverflow.com/questions/2089083/pure-virtual-function-with-implementation – patros

+5

@patros: Không có câu trả lời nào trong chuỗi đó mô tả trường hợp sử dụng thực tế *. Vì vậy, không, tôi không nghĩ rằng đây là một dupe. – missingfaktor

Trả lời

31

cổ điển là một destructor ảo tinh khiết:

class abstract { 
    public: 
    virtual ~abstract() = 0; 
}; 

abstract::~abstract() {} 

Bạn làm cho nó thuần túy bởi vì không có gì khác để làm như vậy, và bạn muốn lớp là trừu tượng, nhưng bạn phải cung cấp một thực hiện tuy nhiên, vì các destructors của các lớp dẫn xuất gọi cho bạn một cách rõ ràng. Vâng, tôi biết, một ví dụ sách giáo khoa khá ngớ ngẩn, nhưng như vậy nó là một cổ điển. Nó phải có trong ấn bản đầu tiên của The C++ Programming Language.

Dù sao, tôi không thể nhớ là có bao giờ thực sự cần khả năng triển khai chức năng ảo thuần túy hay không. Đối với tôi, có vẻ như lý do duy nhất mà tính năng này có là vì nó sẽ phải được cho phép một cách rõ ràng và Stroustrup không thấy lý do nào cho điều đó.

Nếu bạn cảm thấy mình cần tính năng này, có thể bạn đang đi sai đường với thiết kế của mình.

+3

Một vấn đề mà một downvoter có thể có (đối với bản ghi, tôi đã không downvote) là có thực sự không có lý do mạnh mẽ để có một destructor ảo tinh khiết. Nếu có một hàm ảo thuần túy khác, không có lý do gì cho dtor là virtual thuần túy (mặc dù nó gần như chắc chắn là ảo).Nếu dtor là hàm ảo thuần túy duy nhất, thì có thể thực sự không cần lớp trừu tượng. –

+1

@Michael: Tôi biết phê phán này. (Trong thực tế, tôi đã viết khá nhiều rằng trong câu trả lời của tôi.) Vấn đề là, tôi không thể nghĩ ra bất kỳ lý do tốt để thực hiện một chức năng ảo tinh khiết bản thân mình. Như tôi đã viết, nếu bạn cảm thấy bạn cần phải làm điều đó, rất có thể bạn đang đi sai đường. – sbi

+0

@MichaelBurr thật lố bịch. nó quên đi tất cả các chất liệu khác không phải là phương pháp. bạn có thể có một lớp trừu tượng cơ sở cung cấp cho bạn một số trường dữ liệu (vì sao phải sao chép khi nó được chia sẻ bởi các dẫn xuất) và khai báo một giao diện hoặc một khái niệm cùng một lúc. –

27

Các hàm ảo thuần túy có hoặc không có phần thân chỉ đơn giản có nghĩa là các kiểu dẫn xuất phải cung cấp việc triển khai riêng của chúng.

Cơ quan chức năng ảo thuần túy trong lớp cơ sở rất hữu ích nếu các lớp dẫn xuất của bạn muốn gọi triển khai lớp cơ sở của bạn.

8

Một trường hợp sử dụng đang gọi hàm ảo thuần túy từ hàm tạo hoặc hàm hủy của lớp.

+1

+1: Đây thực ra là trường hợp chính. Một số có thể nói trường hợp duy nhất. –

+0

Nhưng không có điều gì như một nhà xây dựng ảo thuần túy, mà bài viết của bạn có vẻ hàm ý. –

+3

@ John: Tôi đọc nó khác. Tuy nhiên, tôi nghĩ rằng gọi các chức năng ảo, thuần khiết hay không, từ ctors hoặc detors là một thực tế khá đáng ngờ anyway. – sbi

4

Sự khác biệt duy nhất của chức năng ảo với cơ thể và chức năng ảo thuần túy với cơ thể là sự tồn tại của sự ngăn chặn thứ hai. Bạn không thể đánh dấu abstract trừu tượng trong C++.

16

Một lý do khiến lớp cơ sở trừu tượng (với chức năng ảo thuần túy) có thể cung cấp một thực thi cho hàm ảo thuần túy mà nó khai báo là cho phép các lớp dẫn xuất có 'mặc định' dễ dàng mà họ có thể chọn sử dụng. Không có nhiều lợi thế so với chức năng ảo bình thường có thể được ghi đè tùy chọn - trên thực tế, sự khác biệt thực sự duy nhất là bạn buộc lớp bắt nguồn phải rõ ràng về việc sử dụng lớp cơ sở 'mặc định' :

class foo { 
public: 
    virtual int interface(); 
}; 

int foo::interface() 
{ 
    printf("default foo::interface() called\n"); 
    return 0; 
}; 


class pure_foo { 
public: 
    virtual int interface() = 0; 
}; 

int pure_foo::interface() 
{ 
    printf("default pure_foo::interface() called\n"); 
    return 42; 
} 

//------------------------------------ 

class foobar : public foo { 
    // no need to override to get default behavior 
}; 

class foobar2 : public pure_foo { 
public: 
    // need to be explicit about the override, even to get default behavior 
    virtual int interface(); 
}; 

int foobar2::interface() 
{ 
    // foobar is lazy; it'll just use pure_foo's default 
    return pure_foo::interface(); 
} 

Tôi không chắc chắn có rất nhiều lợi ích - có thể trong trường hợp thiết kế bắt đầu với lớp trừu tượng, sau đó cho thấy rất nhiều lớp bê tông có nguồn gốc thực hiện cùng một hành vi , vì vậy họ đã quyết định chuyển hành vi đó thành một lớp thực hiện cơ sở cho hàm ảo thuần túy.

Tôi cho rằng nó cũng có thể là hợp lý để đặt hành vi chung vào việc thực hiện lớp cơ sở của hàm ảo thuần túy mà các lớp dẫn xuất có thể được mong đợi sửa đổi/tăng cường/tăng thêm.

+0

@RichardGeorge: bạn có thể giải thích những gì không thực hiện đúng không? Nó hoạt động như tôi mong đợi, nhưng có lẽ bạn đang xem xét một trường hợp sử dụng khác với tôi. –

+0

@Micheal, nó hoạt động tốt, tôi hơi sai lầm với trường hợp sử dụng của riêng tôi, 1 cho bạn. –

7

Herb Sutter toàn năng, cựu chủ tịch của ủy ban tiêu chuẩn C++, did give 3 scenarios nơi bạn có thể xem xét việc cung cấp triển khai cho các phương pháp ảo thuần túy.

Gotta nói rằng cá nhân - Tôi thấy không ai trong số họ thuyết phục, và thường coi đây là một trong những mụn cóc ngữ nghĩa của C++.Có vẻ như C++ không thể xây dựng và tách rời các vtables trừu tượng, chỉ cho họ thấy một thời gian ngắn khi xây dựng/phá hủy trẻ em, and then các chuyên gia cộng đồng nhất trí giới thiệu never to use them.

+1

Bài viết Sutter đó là tuyệt vời. Đã xóa một số ý tưởng mơ hồ đối với tôi. – DarenW

+0

Tôi không thấy mối quan hệ với vtables trừu tượng-cha mẹ. – curiousguy

2

Câu hỏi này thực sự khó hiểu khi học OOD và C++. Cá nhân, một điều liên tục đến trong đầu tôi là một cái gì đó như: Nếu tôi cần một chức năng Pure ảo để thực hiện, vậy tại sao làm cho nó "Tinh khiết" ở vị trí đầu tiên? Tại sao không chỉ để lại nó chỉ "ảo" và có nguồn gốc cả lợi ích và ghi đè lên việc thực hiện cơ sở?

Sự nhầm lẫn xuất phát từ thực tế là nhiều nhà phát triển coi cơ thể/triển khai không phải là mục tiêu chính/lợi ích của việc xác định hàm ảo thuần túy. Đây không phải là sự thật! Sự vắng mặt của cơ thể trong hầu hết các trường hợp là hậu quả logic của việc có một hàm ảo thuần túy. Lợi ích chính của việc có một chức năng ảo thuần túy là xác định một HỢP ĐỒNG! Bằng cách định nghĩa một hàm ảo thuần túy, bạn muốn FORCE mọi dẫn xuất đến LUÔN LUÔN cung cấp khả năng thực thi hàm của chúng. "Khía cạnh HỢP ĐỒNG" này rất quan trọng đặc biệt nếu bạn đang phát triển một thứ gì đó giống như API công khai. Làm cho hàm chỉ ảo không đủ vì các dẫn xuất không còn bị buộc phải cung cấp việc thực hiện riêng của chúng, do đó bạn có thể mất đi khía cạnh hợp đồng (điều này có thể bị hạn chế trong trường hợp API công khai). Như thường nói: "Chức năng ảo có thể bị ghi đè, các hàm Pure ảo PHẢI được ghi đè." Và trong hầu hết các trường hợp, các hợp đồng là các khái niệm trừu tượng, do đó, nó không có ý nghĩa đối với các hàm ảo thuần túy tương ứng để có bất kỳ việc thực hiện nào.

Nhưng đôi khi, và bởi vì cuộc sống là lạ, bạn có thể muốn thiết lập một hợp đồng mạnh mẽ giữa các phái sinh và cũng muốn họ bằng cách nào đó hưởng lợi từ việc thực hiện mặc định trong khi xác định hành vi của mình cho hợp đồng. Ngay cả khi hầu hết các tác giả sách khuyên bạn nên tránh để mình vào những tình huống này, ngôn ngữ cần thiết để cung cấp một mạng lưới an toàn để ngăn chặn điều tồi tệ nhất! Một chức năng ảo đơn giản sẽ không đủ vì có thể có nguy cơ thoát khỏi hợp đồng. Vì vậy, giải pháp C++ đã cung cấp là cho phép các hàm ảo thuần túy cũng có thể cung cấp một thực thi mặc định.

Bài viết Sutter được trích dẫn ở trên cho các trường hợp sử dụng thú vị có chức năng Pure Virtual với nội dung.

+0

Bài viết về Sutter ở đâu? Tôi không nhìn thấy nó ngoài nhận xét của bạn trong câu cuối cùng * "... bài viết của Sutter được trích dẫn ở trên ..." *. – jww

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