Ảo / ảo thuần giải thích


345

Chính xác thì nó có nghĩa gì nếu một hàm được định nghĩa là ảo và nó có giống như ảo thuần không?

Câu trả lời:


338

Từ chức năng ảo của Wikipedia ...

Trong lập trình hướng đối tượng, trong các ngôn ngữ như C ++ và Object Pascal, một hàm ảo hoặc phương thức ảo là một hàm hoặc phương thức có thể thừa kế và có thể ghi đè. Khái niệm này là một phần quan trọng của phần đa hình (thời gian chạy) của lập trình hướng đối tượng (OOP). Nói tóm lại, một hàm ảo định nghĩa một hàm mục tiêu sẽ được thực thi, nhưng mục tiêu có thể không được biết đến tại thời điểm biên dịch.

Không giống như một hàm không ảo, khi một hàm ảo bị ghi đè, phiên bản có nguồn gốc nhất được sử dụng ở tất cả các cấp của hệ thống phân cấp lớp, thay vì chỉ ở mức mà nó được tạo. Do đó, nếu một phương thức của lớp cơ sở gọi một phương thức ảo, phiên bản được định nghĩa trong lớp dẫn xuất sẽ được sử dụng thay vì phiên bản được định nghĩa trong lớp cơ sở.

Điều này trái ngược với các hàm không ảo, vẫn có thể bị ghi đè trong lớp dẫn xuất, nhưng phiên bản "mới" sẽ chỉ được sử dụng bởi lớp dẫn xuất và bên dưới, nhưng sẽ không thay đổi chức năng của lớp cơ sở.

trong khi..

Hàm ảo thuần túy hoặc phương thức ảo thuần túy là một hàm ảo được yêu cầu thực hiện bởi lớp dẫn xuất nếu lớp dẫn xuất không trừu tượng.

Khi một phương thức ảo thuần túy tồn tại, lớp "trừu tượng" và không thể tự khởi tạo. Thay vào đó, một lớp dẫn xuất thực hiện (các) phương thức thuần ảo phải được sử dụng. Một ảo thuần không được định nghĩa trong lớp cơ sở, vì vậy một lớp dẫn xuất phải định nghĩa nó hoặc lớp dẫn xuất đó cũng trừu tượng và không thể khởi tạo được. Chỉ có một lớp không có phương thức trừu tượng có thể được khởi tạo.

Một ảo cung cấp một cách để ghi đè chức năng của lớp cơ sở và một ảo thuần đòi hỏi nó.


10
Vậy ... ảo thuần túy là một từ khóa, hay chỉ là một thuật ngữ được sử dụng?
Justin

197
Hàm void ảo () = 0; là một ảo thuần túy. "= 0" biểu thị độ tinh khiết.
Goz

8
Justin, 'thuần ảo' chỉ là một thuật ngữ (không phải từ khóa, xem câu trả lời của tôi bên dưới) được sử dụng có nghĩa là "chức năng này không thể được thực hiện bởi lớp cơ sở. Như Goz đã nói, thêm" = 0 "vào cuối ảo chức năng làm cho nó "tinh khiết"
Nick Haddad

14
Tôi tin rằng Stroustrup nói rằng anh ấy muốn thêm một puretừ khóa, nhưng Bell Labs sắp phát hành C ++ và người quản lý của anh ấy sẽ không cho phép nó ở giai đoạn cuối. Thêm từ khóa là một vấn đề lớn.
quark

14
Đây không phải là một câu trả lời tốt. Bất kỳ phương pháp nào cũng có thể được ghi đè, không chỉ là ảo. Xem câu trả lời của tôi để biết thêm chi tiết.
Asik

212

Tôi muốn bình luận về định nghĩa ảo của Wikipedia, như được lặp lại bởi một số ở đây. [Tại thời điểm câu trả lời này được viết,] Wikipedia đã định nghĩa một phương thức ảo là một phương thức có thể được ghi đè trong các lớp con. [May mắn thay, Wikipedia đã được chỉnh sửa từ đó và bây giờ nó giải thích điều này một cách chính xác.] Điều đó không chính xác: bất kỳ phương thức nào, không chỉ là ảo, đều có thể bị ghi đè trong các lớp con. Những gì ảo làm là cung cấp cho bạn tính đa hình, nghĩa là khả năng chọn trong thời gian chạy ghi đè có nguồn gốc nhất của một phương thức .

Hãy xem xét các mã sau đây:

#include <iostream>
using namespace std;

class Base {
public:
    void NonVirtual() {
        cout << "Base NonVirtual called.\n";
    }
    virtual void Virtual() {
        cout << "Base Virtual called.\n";
    }
};
class Derived : public Base {
public:
    void NonVirtual() {
        cout << "Derived NonVirtual called.\n";
    }
    void Virtual() {
        cout << "Derived Virtual called.\n";
    }
};

int main() {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    bBase->NonVirtual();
    bBase->Virtual();
    bDerived->NonVirtual();
    bDerived->Virtual();
}

Đầu ra của chương trình này là gì?

Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.

Xuất phát ghi đè mọi phương thức của Base: không chỉ ảo mà còn không ảo.

Chúng tôi thấy rằng khi bạn có Base-trỏ-to-Derogen (bDerive), gọi NonVirtual gọi thực hiện lớp Base. Điều này được giải quyết tại thời gian biên dịch: trình biên dịch thấy rằng bDerive là một Base *, NonVirtual không phải là ảo, do đó, nó thực hiện độ phân giải trên lớp Base.

Tuy nhiên, gọi Virtual gọi thực hiện lớp Derogen. Do từ khóa ảo, việc lựa chọn phương thức xảy ra vào thời gian chạy , không phải thời gian biên dịch. Điều xảy ra ở đây vào thời gian biên dịch là trình biên dịch thấy rằng đây là Base * và nó đang gọi một phương thức ảo, vì vậy nó chèn một cuộc gọi đến vtable thay vì lớp Base. Vtable này được khởi tạo tại thời gian chạy, do đó độ phân giải thời gian chạy thành ghi đè có nguồn gốc nhất.

Tôi hy vọng điều này không quá khó hiểu. Nói tóm lại, bất kỳ phương thức nào cũng có thể bị ghi đè, nhưng chỉ có các phương thức ảo cung cấp cho bạn tính đa hình, nghĩa là lựa chọn thời gian chạy của ghi đè có nguồn gốc nhất. Tuy nhiên, trên thực tế, việc ghi đè một phương thức không ảo được coi là thực tiễn xấu và hiếm khi được sử dụng, vì vậy nhiều người (bao gồm cả những người đã viết bài viết trên Wikipedia) nghĩ rằng chỉ có các phương thức ảo mới có thể bị ghi đè.


6
Chỉ vì bài viết Wikipedia (mà tôi không có cách nào bảo vệ) định nghĩa một phương thức ảo "như một phương thức có thể bị ghi đè trong các lớp con" không loại trừ khả năng các phương thức khác, không ảo, có cùng tên có thể được khai báo. Điều này được gọi là quá tải.

26
Định nghĩa dù sao cũng không chính xác. Một phương thức có thể được ghi đè trong một lớp dẫn xuất không phải là ảo theo định nghĩa; liệu phương thức có thể bị ghi đè không liên quan đến định nghĩa "ảo". Ngoài ra, "nạp chồng" thường đề cập đến việc có nhiều phương thức có cùng tên và kiểu trả về nhưng các đối số khác nhau, trong cùng một lớp; nó rất khác với "ghi đè" ngụ ý chính xác cùng một chữ ký nhưng trong một lớp dẫn xuất. Khi nó được thực hiện không đa hình (cơ sở không ảo), nó thường được gọi là "ẩn".
Asik

5
Đây phải là câu trả lời được chấp nhận. Bài viết Wikipedia cụ thể mà tôi sẽ dành thời gian để liên kết ở đây vì không ai khác trong câu hỏi này đã làm nó , là rác hoàn chỉnh. +1, thưa ngài.
josaphatv

2
BÂY GIỜ nó có ý nghĩa. Xin cảm ơn, thưa ngài, vì đã giải thích chính xác rằng bất kỳ phương thức nào cũng có thể bị ghi đè bởi các lớp dẫn xuất và sự thay đổi là cách trình biên dịch sẽ hành xử để chọn hàm nào được gọi trong các tình huống khác nhau.
Doodad

3
Nó có thể hữu ích để thêm một Derived*với các cuộc gọi chức năng tương tự để lái xe về nhà điểm. Nếu không thì câu trả lời tuyệt vời
Jeff Jones

114

Từ khóa ảo cung cấp cho C ++ khả năng hỗ trợ đa hình. Khi bạn có một con trỏ tới một đối tượng của một số lớp, chẳng hạn như:

class Animal
{
  public:
    virtual int GetNumberOfLegs() = 0;
};

class Duck : public Animal
{
  public:
     int GetNumberOfLegs() { return 2; }
};

class Horse : public Animal
{
  public:
     int GetNumberOfLegs() { return 4; }
};

void SomeFunction(Animal * pAnimal)
{
  cout << pAnimal->GetNumberOfLegs();
}

Trong ví dụ (ngớ ngẩn) này, hàm GetNumberOfLegs () trả về số thích hợp dựa trên lớp của đối tượng mà nó được gọi.

Bây giờ, hãy xem xét chức năng 'Một số chức năng'. Nó không quan tâm loại vật thể động vật nào được truyền cho nó, miễn là nó có nguồn gốc từ Động vật. Trình biên dịch sẽ tự động chuyển bất kỳ lớp có nguồn gốc Động vật nào sang Động vật vì nó là lớp cơ sở.

Nếu chúng ta làm điều này:

Duck d;
SomeFunction(&d);

nó sẽ xuất '2'. Nếu chúng ta làm điều này:

Horse h;
SomeFunction(&h);

nó sẽ xuất '4'. Chúng tôi không thể làm điều này:

Animal a;
SomeFunction(&a);

bởi vì nó sẽ không biên dịch do hàm ảo GetNumberOfLegs () là thuần túy, có nghĩa là nó phải được thực hiện bằng cách lấy các lớp (các lớp con).

Hàm ảo thuần túy hầu hết được sử dụng để định nghĩa:

a) các lớp trừu tượng

Đây là các lớp cơ sở nơi bạn phải xuất phát từ chúng và sau đó thực hiện các hàm ảo thuần túy.

b) giao diện

Đây là các lớp 'trống' trong đó tất cả các hàm là thuần ảo và do đó bạn phải rút ra và sau đó thực hiện tất cả các hàm.


Trong ví dụ của bạn, bạn không thể làm số 4 vì bạn không cung cấp triển khai phương thức ảo thuần túy. Nó không hoàn toàn bởi vì phương thức này là thuần ảo.
iheanyi

@iheanyi Bạn không thể cung cấp triển khai cho phương thức ảo thuần túy trong lớp cơ sở. Do đó trường hợp # 4 vẫn còn lỗi.
prasad

32

Trong một lớp C ++, ảo là từ khóa chỉ định rằng, một phương thức có thể được ghi đè (tức là được thực hiện bởi) một lớp con. Ví dụ:

class Shape 
{
  public:
    Shape();
    virtual ~Shape();

    std::string getName() // not overridable
    {
      return m_name;
    }

    void setName( const std::string& name ) // not overridable
    {
      m_name = name;
    }

  protected:
    virtual void initShape() // overridable
    {
      setName("Generic Shape");
    }

  private:
    std::string m_name;
};

Trong trường hợp này, một lớp con có thể ghi đè hàm initShape để thực hiện một số công việc chuyên biệt:

class Square : public Shape
{
  public: 
    Square();
    virtual ~Square();

  protected:
    virtual void initShape() // override the Shape::initShape function
    {
      setName("Square");
    }
}

Thuật ngữ thuần ảo dùng để chỉ các hàm ảo cần được thực hiện bởi một lớp con và chưa được lớp cơ sở triển khai. Bạn chỉ định một phương thức là thuần ảo bằng cách sử dụng từ khóa ảo và thêm a = 0 vào cuối khai báo phương thức.

Vì vậy, nếu bạn muốn tạo Shape :: initShape thuần ảo, bạn sẽ làm như sau:

class Shape 
{
 ...
    virtual void initShape() = 0; // pure virtual method
 ... 
};

Bằng cách thêm một phương thức ảo thuần túy vào lớp của bạn, bạn tạo cho lớp một lớp cơ sở trừu tượng rất tiện dụng để tách các giao diện khỏi việc thực hiện.


1
Về "các hàm ảo phải được thực hiện bởi một lớp con" - điều đó không hoàn toàn đúng, nhưng lớp con cũng trừu tượng nếu không. Và các lớp trừu tượng không thể được khởi tạo. Ngoài ra, "không thể được thực hiện bởi lớp cơ sở" có vẻ sai lệch; Tôi đề nghị rằng "đã không được" sẽ tốt hơn vì không có hạn chế nào đối với việc sửa đổi mã để thêm một triển khai trong lớp cơ sở.
NVRAM

2
Và "hàm getName không thể được thực hiện bởi một lớp con" không hoàn toàn đúng. Các lớp con có thể thực hiện phương thức (w / chữ ký giống hoặc khác) nhưng việc thực hiện đó sẽ không QUÁ TRÌNH phương thức. Bạn có thể triển khai Circle như một lớp con và triển khai "std :: string Circle :: getName ()" - sau đó bạn có thể gọi một trong hai phương thức cho một thể hiện Circle. Nhưng nếu được sử dụng thông qua con trỏ Shape hoặc tham chiếu, trình biên dịch sẽ gọi Shape :: getName ().
NVRAM

1
Điểm tốt trên cả hai mặt trận. Tôi đã cố gắng tránh xa việc thảo luận về các trường hợp đặc biệt cho ví dụ này, tôi sẽ sửa đổi câu trả lời để dễ tha thứ hơn. Cảm ơn!
Nick Haddad

@NickHaddad Chủ đề cũ, nhưng tự hỏi tại sao bạn gọi biến của mình m_name. Có m_nghĩa là gì?
Tqn

1
@Tqn giả sử NickHaddad đã tuân theo các quy ước, m_name là một quy ước đặt tên thường được gọi là ký hiệu Hungary. M chỉ thành viên của cấu trúc / lớp, số nguyên.
Ketcomp

16

"Ảo" có nghĩa là phương thức có thể bị ghi đè trong các lớp con, nhưng có một triển khai có thể gọi trực tiếp trong lớp cơ sở. "Pure virtual" có nghĩa là nó là một phương thức ảo không có triển khai có thể gọi trực tiếp. Một phương thức như vậy phải được ghi đè ít nhất một lần trong hệ thống phân cấp thừa kế - nếu một lớp có bất kỳ phương thức ảo nào chưa được thực hiện, các đối tượng của lớp đó không thể được xây dựng và quá trình biên dịch sẽ thất bại.

@quark chỉ ra rằng các phương thức thuần ảo có thể có một triển khai, nhưng vì các phương thức thuần ảo phải bị ghi đè, nên việc thực hiện mặc định không thể được gọi trực tiếp. Dưới đây là một ví dụ về phương thức thuần ảo với mặc định:

#include <cstdio>

class A {
public:
    virtual void Hello() = 0;
};

void A::Hello() {
    printf("A::Hello\n");
}

class B : public A {
public:
    void Hello() {
        printf("B::Hello\n");
        A::Hello();
    }
};

int main() {
    /* Prints:
           B::Hello
           A::Hello
    */
    B b;
    b.Hello();
    return 0;
}

Theo ý kiến, việc biên dịch có thất bại hay không là do trình biên dịch cụ thể. Trong GCC 4.3.3 ít nhất, nó sẽ không biên dịch:

class A {
public:
    virtual void Hello() = 0;
};

int main()
{
    A a;
    return 0;
}

Đầu ra:

$ g++ -c virt.cpp 
virt.cpp: In function int main()’:
virt.cpp:8: error: cannot declare variable a to be of abstract type A
virt.cpp:1: note:   because the following virtual functions are pure within A’:
virt.cpp:3: note:   virtual void A::Hello()

nó phải được ghi đè nếu bạn muốn khởi tạo một thể hiện của lớp. Nếu bạn không tạo bất kỳ trường hợp nào thì mã sẽ biên dịch tốt.
Glen

1
biên dịch sẽ không thất bại. Nếu không có triển khai một phương thức ảo (thuần túy) thì lớp / đối tượng đó không thể được khởi tạo. Nó có thể không LINK, nhưng nó sẽ biên dịch.
Tim

@Glen, @tim: trên trình biên dịch nào? Khi tôi cố gắng biên dịch một chương trình xây dựng một lớp trừu tượng, nó không biên dịch.
John Millikin

@John Compilation sẽ chỉ thất bại nếu bạn cố gắng khởi tạo một thể hiện của một lớp có chứa PVF. Tất nhiên bạn có thể khởi tạo con trỏ hoặc giá trị tham chiếu cho các lớp đó.

5
Ngoài ra, John, điều sau đây không hoàn toàn đúng: "'Ảo ảo' có nghĩa là nó là một phương thức ảo không có triển khai." Phương pháp ảo thuần túy có thể có triển khai. Nhưng bạn không thể gọi họ trực tiếp: bạn phải ghi đè và sử dụng triển khai lớp cơ sở từ bên trong lớp con. Điều này cho phép bạn cung cấp một phần mặc định của việc thực hiện. Đó không phải là một kỹ thuật phổ biến mặc dù.
quark

9

Làm thế nào để từ khóa ảo hoạt động?

Giả sử rằng Con người là một lớp cơ sở, Ấn Độ có nguồn gốc từ con người.

Class Man
{
 public: 
   virtual void do_work()
   {}
}

Class Indian : public Man
{
 public: 
   void do_work()
   {}
}

Khai báo do_work () dưới dạng ảo đơn giản có nghĩa là: mà do_work () sẽ gọi sẽ được xác định CHỈ vào thời gian chạy.

Giả sử tôi làm

Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.

Nếu ảo không được sử dụng, tương tự được xác định tĩnh hoặc bị ràng buộc tĩnh bởi trình biên dịch, tùy thuộc vào đối tượng nào đang gọi. Vì vậy, nếu một đối tượng của Man gọi do_work (), Man's do_work () được gọi là NGAY CẢ NHỮNG ĐIỂM NÀO ĐỐI VỚI MỘT ĐỐI TƯỢNG ẤN ĐỘ

Tôi tin rằng câu trả lời được bình chọn hàng đầu là sai lệch - Bất kỳ phương thức nào dù ảo hay không đều có thể có một triển khai được ghi đè trong lớp dẫn xuất. Với tham chiếu cụ thể đến C ++, sự khác biệt chính xác là thời gian chạy (khi sử dụng ảo) ràng buộc và thời gian biên dịch (khi ảo không được sử dụng nhưng một phương thức bị ghi đè và một con trỏ cơ sở được trỏ vào một đối tượng dẫn xuất) liên kết các hàm liên quan.

Dường như có một bình luận sai lệch khác nói rằng,

"Justin, 'thuần ảo' chỉ là một thuật ngữ (không phải từ khóa, xem câu trả lời của tôi bên dưới) được sử dụng có nghĩa là" chức năng này không thể được thực hiện bởi lớp cơ sở. "

CÁI NÀY SAI! Các chức năng ảo hoàn toàn cũng có thể có một cơ thể VÀ CÓ THỂ THỰC HIỆN! Sự thật là hàm ảo thuần của một lớp trừu tượng có thể được gọi là tĩnh! Hai tác giả rất giỏi là Bjarne Stroustrup và Stan Lippman .... vì họ đã viết ngôn ngữ này.


2
Thật không may khi một câu trả lời bắt đầu được nâng cấp, tất cả những câu hỏi khác sẽ bị bỏ qua. Thậm chí tho họ có thể tốt hơn.
LtWorf

3

Hàm ảo là một hàm thành viên được khai báo trong lớp cơ sở và được xác định lại bởi lớp dẫn xuất. Hàm ảo được phân cấp theo thứ tự kế thừa. Khi một lớp dẫn xuất không ghi đè một hàm ảo, hàm được định nghĩa trong lớp cơ sở của nó được sử dụng.

Hàm ảo thuần túy là một hàm không chứa định nghĩa liên quan đến lớp cơ sở. Nó không có thực hiện trong lớp cơ sở. Bất kỳ lớp dẫn xuất nào cũng phải ghi đè hàm này.


2

Simula, C ++ và C #, sử dụng ràng buộc phương thức tĩnh theo mặc định, lập trình viên có thể chỉ định các phương thức cụ thể đó nên sử dụng liên kết động bằng cách gắn nhãn chúng là ảo. Liên kết phương thức động là trung tâm của lập trình hướng đối tượng.

Lập trình hướng đối tượng đòi hỏi ba khái niệm cơ bản: đóng gói, kế thừa và ràng buộc phương thức động.

Đóng gói cho phép các chi tiết thực hiện của một sự trừu tượng được ẩn đằng sau một giao diện đơn giản.

Kế thừa cho phép một sự trừu tượng mới được định nghĩa là một phần mở rộng hoặc sàng lọc của một số trừu tượng hiện có, có được một số hoặc tất cả các đặc tính của nó một cách tự động.

Liên kết phương thức động cho phép sự trừu tượng mới hiển thị hành vi mới của nó ngay cả khi được sử dụng trong bối cảnh mong đợi sự trừu tượng cũ.


1

Các phương thức ảo CÓ THỂ được ghi đè bằng cách tạo các lớp, nhưng cần một triển khai trong lớp cơ sở (một lớp sẽ bị ghi đè)

Các phương thức ảo thuần túy không có triển khai lớp cơ sở. Chúng cần được định nghĩa bởi các lớp dẫn xuất. (Vì vậy, kỹ thuật bị ghi đè không phải là thuật ngữ đúng, vì không có gì để ghi đè).

Ảo tương ứng với hành vi java mặc định, khi lớp dẫn xuất ghi đè một phương thức của lớp cơ sở.

Các phương thức Pure Virtual tương ứng với hành vi của các phương thức trừu tượng trong các lớp trừu tượng. Và một lớp chỉ chứa các phương thức và hằng ảo thuần túy sẽ là giá trị cpp cho một Giao diện.


0

Chức năng ảo thuần túy

thử mã này

#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()=0;

};

class anotherClass:aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"hellow World";
    }

};
int main()
{
    //aClassWithPureVirtualFunction virtualObject;
    /*
     This not possible to create object of a class that contain pure virtual function
    */
    anotherClass object;
    object.sayHellow();
}

Trong lớp AnotherClass loại bỏ hàm sayHellow và chạy mã. Bạn sẽ gặp lỗi! Bởi vì khi một lớp chứa một hàm ảo thuần túy, không có đối tượng nào có thể được tạo từ lớp đó và nó được kế thừa thì lớp dẫn xuất của nó phải thực hiện chức năng đó.

Chức năng ảo

thử mã khác

#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()
    {
        cout<<"from base\n";
    }

};

class anotherClass:public aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"from derived \n";
    }

};
int main()
{
    aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction;
    baseObject->sayHellow();///call base one

    baseObject=new anotherClass;
    baseObject->sayHellow();////call the derived one!

}

Ở đây, hàm sayHellow được đánh dấu là ảo trong lớp cơ sở. Nó nói trình biên dịch thử tìm kiếm hàm trong lớp dẫn xuất và thực hiện hàm. Nếu không tìm thấy thì thực hiện cơ sở một. Cảm ơn


Haha, nó đã cho tôi một dài 30 giây để hiểu những gì là sai ở đây ... Hellow :)
hans

0

"Hàm ảo hoặc phương thức ảo là một hàm hoặc phương thức có hành vi có thể bị ghi đè trong một lớp kế thừa bởi một hàm có cùng chữ ký" - wikipedia

Đây không phải là một lời giải thích tốt cho các chức năng ảo. Bởi vì, ngay cả khi một thành viên không ảo, các lớp kế thừa có thể ghi đè lên nó. Bạn có thể thử và xem nó cho mình.

Sự khác biệt thể hiện chính nó khi một hàm lấy một lớp cơ sở làm tham số. Khi bạn cung cấp một lớp kế thừa làm đầu vào, hàm đó sử dụng lớp thực hiện của lớp cơ sở của hàm overriden. Tuy nhiên, nếu hàm đó là ảo, nó sử dụng hàm được triển khai trong lớp dẫn xuất.


0
  • Các hàm ảo phải có một định nghĩa trong lớp cơ sở và cả trong lớp dẫn xuất nhưng không cần thiết, ví dụ hàm ToString () hoặc toString () là một Virtual để bạn có thể cung cấp triển khai của riêng mình bằng cách ghi đè nó trong (các) lớp do người dùng định nghĩa.

  • Các hàm ảo được khai báo và định nghĩa trong lớp bình thường.

  • Hàm ảo thuần túy phải được khai báo kết thúc bằng "= 0" và nó chỉ có thể được khai báo trong lớp trừu tượng.

  • Một lớp trừu tượng có (các) hàm ảo thuần túy không thể có định nghĩa về các hàm ảo thuần đó, do đó, nó ngụ ý rằng việc triển khai phải được cung cấp trong (các) lớp xuất phát từ lớp trừu tượng đó.


Ghi chú tương tự như @rashingcs: Thật vậy, một hàm ảo thuần túy có thể có định nghĩa của nó ...
Jarek C
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.