2010-02-09 26 views
7

Tôi có đoạn mã sau:Sử dụng chức năng ảo trong con sau khi đúc hoạt động trong C++

class A 
{ 
}; 

class B : public A 
{ 
    public: 
     virtual void f() {} 
}; 

int main() 
{ 
    A* a = new A(); 
    B* b = static_cast<B*>(a); 
    b->f(); 
} 

Chương trình này không thành công với một lỗi segmentation. Có hai giải pháp để thực hiện chương trình làm việc này:

  1. tuyên bố f phi ảo
  2. không gọi b-> f() (tức là nó không thất bại vì các diễn viên)

Tuy nhiên , cả hai đều không phải là một lựa chọn. Tôi giả định rằng điều này không hoạt động vì một tra cứu trong vtable.

(Trong chương trình thực tế, A cũng không có chức năng ảo. Ngoài ra, chức năng ảo không được gọi trong constructor.)

Có cách nào để làm cho công việc chương trình này?

+1

+1 cho một câu hỏi 1st có định dạng đúng. –

+0

Trong trường hợp này, bạn có thể thích dynamic_cast, mà sẽ "thất bại" (trả về null) với A * a = new A(), nhưng thành công với A * a = new B(). Bạn sẽ phải kiểm tra rằng b không phải là null trước khi gọi -> f(). – jmanning2k

Trả lời

12

Bạn không thể làm điều đó vì đối tượng bạn tạo là A, không B. cast của bạn là invalid-- một đối tượng của A (tạo ra với mới) không thể kỳ diệu trở thành một đối tượng của B.

Bạn có có nghĩa là A * a = new A() thực sự là A * a = new B()? Trong trường hợp đó, tôi hy vọng nó sẽ hoạt động.

+0

Cảm ơn bạn (tất cả) cho thư trả lời của bạn. Tôi tự hỏi về điều này: Tại sao chương trình này hoạt động nếu tôi bỏ qua từ khóa ảo? (Phần còn lại của mã vẫn không thay đổi.) Tôi đã cố gắng đặt một số chức năng trong f() (một cout đơn giản) và nó được thực hiện. Nếu nó không phải là một đối tượng thuộc loại B, điều này không thể thực hiện được. Đúng? – Alexander

+2

Đây có thể là câu hỏi riêng của nó, nhưng câu trả lời là một cuộc gọi hàm thành viên (không phải ảo) 'b-> f()' là nội bộ giống với 'B :: f (b)'. Nói cách khác, có một hàm duy nhất được gọi là 'B :: f' nhận một đối số đầu tiên ngầm định là một con trỏ tới đối tượng mà nó được gọi (đối số đó là con trỏ' this' và được sử dụng để truy cập các thành viên của đối tượng được đề cập). Với các hàm ảo, nó không được biết trước về việc có nên gọi 'B :: f' hoặc có thể' C :: f' và do đó cần có bảng tra cứu, sử dụng con trỏ 'b' và là nơi segfault của bạn xuất hiện từ. –

+1

(tiếp theo, do ràng buộc về không gian) Vì vậy, với một hàm không phải ảo, lệnh gọi 'b-> f()' (còn gọi là 'B :: f (b)') không thực sự sử dụng * b 'pointer, trừ khi' B :: f'function cố truy cập dữ liệu chứa trong đối tượng 'b'. Nếu phần tử 'B :: f' chỉ đơn giản là một câu lệnh' cout', thì sẽ không có vấn đề rõ ràng. Ví dụ, nếu bạn định nghĩa một thành phần integer bên trong lớp 'B' và cố gắng in ra trong' B :: f', bạn có thể sẽ nhận được một giá trị segfault hoặc một giá trị bất ngờ kỳ lạ cho số nguyên đó. –

2

Để thực hiện static_cast, bạn phải chắc chắn rằng đối tượng có thể được đúc, tức là đối tượng của lớp B.

Trong trường hợp này, tôi chắc chắn rằng nó không phải là.

3

Bạn không thể làm điều đó.

Trong ví dụ của bạn, một là một đối tượng của lớp A. Không B. Đúc nó đến B không làm cho nó một B.

Nếu bạn muốn sử dụng hành vi đối tượng đa hình, sau đó bạn có thể cung cấp chức năng ảo f đến lớp A, và bạn có thể sử dụng mã như A* a = new B(); Sau đó, bạn có thể sử dụng các chức năng ảo thông qua một con trỏ để có được hành vi từ lớp B.

3

Trong mã của bạn:

A* a = new A(); 

bạn tạo một đối tượng A. Sau đó, bạn cố gắng sử dụng static_cast để đi từ một loại cơ sở để một loại hình xuất phát:

B* b = static_cast<B*>(a); 

Nếu giá trị trong a chỉ vào một đối tượng mà thực sự là loại B, đây sẽ là hợp pháp và được hình thành. Nhưng a không trỏ đến một đối tượng thuộc loại B, nó trỏ đến một A, do đó, diễn viên gợi lên hành vi không xác định.

Khắc phục là thay đổi cách bạn khởi tạo đối tượng. Thay đổi:

A* a = new A(); 

... để:

A* a = new B(); 
Các vấn đề liên quan