2012-02-24 59 views
16
class base{ 
    ..... 
    virtual void function1(); 
    virtual void function2(); 
}; 

class derived::public base{ 
    int function1(); 
    int function2(); 
}; 

int main() 
{ 
    derived d; 
    base *b = &d; 
    int k = b->function1() // Why use this instead of the following line? 
    int k = d.function1(); // With this, the need for virtual functions is gone, right? 

} 

Tôi không phải là kỹ sư CompSci và tôi muốn biết điều này. Tại sao lại sử dụng các hàm ảo nếu chúng ta có thể tránh các con trỏ lớp cơ sở?Tại sao sử dụng con trỏ lớp cơ sở cho các lớp dẫn xuất

Trả lời

47

Sức mạnh của đa hình không thực sự là appa thuê trong ví dụ đơn giản của bạn, nhưng nếu bạn mở rộng nó một chút nó có thể trở nên rõ ràng hơn.

class vehicle{ 
     ..... 
     virtual int getEmission(); 
} 

class car : public vehicle{ 
     int getEmission(); 
} 

class bus : public vehicle{ 
     int getEmission(); 
} 

int main() 
{ 
     car a; 
     car b; 
     car c; 
     bus d; 
     bus e; 

     vehicle *traffic[]={&a,&b,&c,&d,&e}; 

     int totalEmission=0; 

     for(int i=0;i<5;i++) 
     { 
      totalEmission+=traffic[i]->getEmission(); 
     } 

} 

Điều này cho phép bạn lặp qua danh sách các con trỏ và có các phương thức khác nhau được gọi tùy thuộc vào loại cơ bản. Về cơ bản nó cho phép bạn viết mã mà bạn không cần phải biết kiểu con là lúc biên dịch, nhưng mã sẽ thực hiện đúng chức năng.

+6

Đây có thể là một trong những ví dụ tốt nhất cho việc sử dụng các chức năng ảo. Cảm ơn! – Garfield

+0

Câu trả lời rõ ràng, nhưng khi tôi đã biết tôi phải thêm phát thải cho tất cả các đối tượng lớp này, tại sao tôi không thể tự tạo các đối tượng cho cả 'xe hơi' và 'xe buýt' và thêm chúng bình thường? Tại sao tôi cần con trỏ kiểu lớp cơ sở. – Yankee

+0

Giả sử nếu chúng ta không sử dụng hàm ảo, lợi ích của việc sử dụng con trỏ lớp cơ sở cho lớp dẫn xuất là gì? – Rajesh

4

Bạn chính xác, nếu bạn có một đối tượng bạn không cần phải tham chiếu đến nó thông qua con trỏ. Bạn cũng không cần một destructor ảo khi đối tượng sẽ bị phá hủy như loại nó đã được tạo ra.

Tiện ích này xuất hiện khi bạn lấy con trỏ đến một đối tượng từ một đoạn mã khác, và bạn thực sự không biết loại có nguồn gốc nhiều nhất là gì. Bạn có thể có hai hoặc nhiều kiểu dẫn xuất được xây dựng trên cùng một cơ sở và có một hàm trả về một con trỏ tới kiểu cơ sở. Các chức năng ảo sẽ cho phép bạn sử dụng con trỏ mà không phải lo lắng về loại bắt nguồn nào bạn đang sử dụng, cho đến khi đến lúc phá hủy đối tượng. Trình phá hủy ảo sẽ phá hủy đối tượng mà bạn không biết lớp đó bắt nguồn từ nó.

Dưới đây là ví dụ đơn giản nhất của việc sử dụng các chức năng ảo:

base *b = new derived; 
b->function1(); 
delete b; 
+1

tôi nghĩ rằng câu hỏi của anh là lý do tại sao sử dụng con trỏ lớp cơ sở và không lý do tại sao hàm hủy ảo –

1

của nó để thực hiện đa hình. Trừ khi bạn có con trỏ cơ sở lớp trỏ đến đối tượng có nguồn gốc, bạn không thể có đa hình ở đây.

One of the key features of derived classes is that a pointer to a derived class is type-compatible with a pointer to its base class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature, that brings Object Oriented Methodologies to its full potential.

In C++, a special type/subtype relationship exists in which a base class pointer or a reference can address any of its derived class subtypes without programmer intervention. This ability to manipulate more than one type with a pointer or a reference to a base class is spoken of as polymorphism.

Subtype polymorphism allows us to write the kernel of our application independent of the individual types we wish to manipulate. Rather, we program the public interface of the base class of our abstraction through base class pointers and references. At run-time, the actual type being referenced is resolved and the appropriate instance of the public interface is invoked. The run-time resolution of the appropriate function to invoke is termed dynamic binding (by default, functions are resolved statically at compile-time). In C++, dynamic binding is supported through a mechanism referred to as class virtual functions. Subtype polymorphism through inheritance and dynamic binding provide the foundation for objectoriented programming

The primary benefit of an inheritance hierarchy is that we can program to the public interface of the abstract base class rather than to the individual types that form its inheritance hierarchy, in this way shielding our code from changes in that hierarchy. We define eval(), for example, as a public virtual function of the abstract Query base class. By writing code such as _rop->eval(); user code is shielded from the variety and volatility of our query language. This not only allows for the addition, revision, or removal of types without requiring changes to user programs, but frees the provider of a new query type from having to recode behavior or actions common to all types in the hierarchy itself. This is supported by two special characteristics of inheritance: polymorphism and dynamic binding. When we speak of polymorphism within C++, we primarily mean the ability of a pointer or a reference of a base class to address any of its derived classes. For example, if we define a nonmember function eval() as follows, // pquery can address any of the classes derived from Query void eval(const Query *pquery) { pquery->eval(); } we can invoke it legally, passing in the address of an object of any of the four query types:

int main() 
{ 
AndQuery aq; 
NotQuery notq; 
OrQuery *oq = new OrQuery; 
NameQuery nq("Botticelli"); // ok: each is derived from Query 
// compiler converts to base class automatically 
eval(&aq); 
eval(&notq); 
eval(oq); 
eval(&nq); 
} 

trong khi một nỗ lực để gọi eval() với địa chỉ của một đối tượng không bắt nguồn từ Query kết quả trong một lỗi thời gian biên dịch:

int main() 
{ string name("Scooby-Doo"); // error: string is not derived from Query 
eval(&name); 
} 

Within eval(), the execution of pquery->eval(); must invoke the appropriate eval() virtual member function based on the actual class object pquery addresses. In the previous example, pquery in turn addresses an AndQuery object, a NotQuery object, an OrQuery object, and a NameQuery object. At each invocation point during the execution of our program, the actual class type addressed by pquery is determined, and the appropriate eval() instance is called. Dynamic binding is the mechanism through which this is accomplished. In the object-oriented paradigm, the programmer manipulates an unknown instance of a bound but infinite set of types. (The set of types is bound by its inheritance hierarchy. In theory, however, there is no limit to the depth and breadth of that hierarchy.) In C++ this is achieved through the manipulation of objects through base class pointers and references only. In the object-based paradigm, the programmer manipulates an instance of a fixed, singular type that is completely defined at the point of compilation. Although the polymorphic manipulation of an object requires that the object be accessed either through a pointer or a reference, the manipulation of a pointer or a reference in C++ does not in itself necessarily result in polymorphism. For example, consider

// no polymorphism 
    int *pi; 
// no language-supported polymorphism 
    void *pvi; 
// ok: pquery may address any Query derivation 
    Query *pquery; 

In C++, polymorphism exists only within individual class hierarchies. Pointers of type void* can be described as polymorphic, but they are without explicit language support — that is, they must be managed by the programmer through explicit casts and some form of discriminant that keeps track of the actual type being addressed.

+0

phần cuối cùng là từ C++ mồi! lấy cuốn sách đó và đọc vào Đa hình –

0

Bạn dường như đã hỏi hai câu hỏi (trong tiêu đề và cuối cùng):

  1. Tại sao sử dụng cơ sở con trỏ lớp cho các lớp học có nguồn gốc? Đây là việc sử dụng đa hình. Nó cho phép bạn xử lý các đối tượng thống nhất trong khi cho phép bạn thực hiện cụ thể. Nếu điều này làm phiền bạn, sau đó tôi giả sử bạn nên hỏi: Tại sao đa hình?

  2. Tại sao sử dụng trình phá hủy ảo nếu chúng ta có thể tránh các con trỏ lớp cơ sở? Vấn đề ở đây là bạn không phải lúc nào cũng tránh các con trỏ lớp cơ sở để khai thác sức mạnh của đa hình.

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