2012-10-24 24 views
9

Nếu tôi có một lớp cơ sở, chỉ với các phương thức ảo và 2 lớp dẫn xuất từ ​​ lớp cơ sở, với những phương thức ảo được triển khai.Con trỏ tới mảng lớp cơ sở, với lớp bắt nguồn

Làm thế nào để:

// causes C2259 
BaseClass* base = new BaseClass[2]; 

BaseClass[0] = new FirstDerivedClass; 
BaseClass[1] = new SecondDerivedClass; 

hay:

// causes "base is being used without being initialized" 
BaseClass* base; 
// causes CC59 again 
BaseClass* base = new BaseClass; 

base[0] = FirstDerivedClass(); 
base[1] = SecondDerivedClass(); 

(hoặc một cái gì đó tương tự)

... để tôi có thể truy cập vào BaseClass s phương pháp thông qua DerivedClass, nhưng bằng con trỏ và con trỏ là một mảng của DerivedClass s?

Trả lời

8

mảng của bạn là loại sai: nó lưu BaseClass đối tượng trường hợp thay vì con trỏ cho chúng. Vì BaseClass có vẻ trừu tượng, trình biên dịch phàn nàn rằng nó không thể xây dựng các trường hợp mặc định để lấp đầy mảng của bạn.

Ngay cả khi BaseClass không trừu tượng, sử dụng mảng đa hình là big no-no trong C++, do đó bạn nên làm mọi thứ khác nhau trong mọi trường hợp.

Fix này bằng cách thay đổi mã để:

BaseClass** base = new BaseClass*[2]; 

BaseClass[0] = new FirstDerivedClass; 
BaseClass[1] = new SecondDerivedClass; 

Điều đó nói rằng, hầu hết thời gian đó là thích hợp hơn để sử dụng std::vector thay vì mảng đơn giản và gợi ý thông minh (như std::shared_ptr) thay vì con trỏ câm. Sử dụng các công cụ này thay vì viết mã theo cách thủ công sẽ chăm sóc một loạt các vấn đề một cách minh bạch với chi phí thời gian chạy cực kỳ nhỏ.

+1

Vấn đề, được gọi là "đối tượng cắt", không phải là cụ thể cho mảng - nó xảy ra bất cứ lúc nào bạn gán một lớp dẫn xuất cho một lớp cơ sở * theo giá trị *. Ví dụ. 'BaseClass b; b = FirstDerivedClass(); 'là compilable C++ mà vẫn phá vỡ âm thầm (tốt nhất bạn có thể nhận được một cảnh báo trình biên dịch). –

+0

Liên kết dường như được di chuyển. – user6003859

+1

@ user6003859 Tôi đã cập nhật nó, cảm ơn vì đã phát hiện ra! – Jon

3

Đó là C++ sử dụng std::vector thay vì mảng đơn giản:

std::vector<BaseClass*> base; 
base.push_back(new FirstDerivedClass()); 
base.push_back(new SecondDerivedClass()); 

Như Kerrek SB nhận thấy phương pháp an toàn nhất là sử dụng std::unique_ptr:

std::vector<std::unique_ptr<BaseClass> > base; 
base.push_back(std_unique_ptr<BaseClass>(new FirstDerivedClass())); 
base.push_back(std_unique_ptr<BaseClass>(new SecondDerivedClass())); 
+2

Nhưng nếu tôi muốn một mảng? – Deukalion

+1

Để làm gì? Tốc độ? Độ tin cậy? –

+0

Điều gì về: BaseClass * Base [2]; Base [0] = new FirstDerivedClass; Cơ sở [1] = new SecondDerivedClass; – Al2O3

2

Nếu BaseClass của bạn có chứa các phương pháp ảo tinh khiết, điều này sẽ không biên dịch:

BaseClass* base = new BaseClass[2]; 

Nếu không, bạn sẽ nhận được rò rỉ bộ nhớ.

Trong C++, điều này được thực hiện bằng cách sử dụng std :: vector hoặc std :: array, với một số loại con trỏ thông minh. Ví dụ:

std::vector< std::shared_ptr<BaseClass> > arr(2); 
arr[0].reset(new FirstDerivedClass()); 
arr[1].reset(new SecondDerivedClass()); 
+0

Một vấn đề thậm chí còn quan trọng hơn với mã OP là * đối tượng cắt *, mà phá vỡ chương trình âm thầm. –

+0

@j_random_hacker Đó là lý do tại sao tôi đề nghị sử dụng một số loại con trỏ thông minh. Lưu trữ các đối tượng của loại cơ sở (nếu nó không có chức năng thuần túy) sẽ gây ra cắt –

0

Đây là câu trả lời (từ Rubby)

BaseClass* Base[2]; 

Base[0] = new FirstDerivedClass; 
Base[1] = new SecondDerivedClass; 
+1

Điều này làm một cái gì đó * rất * khác với những gì mã ban đầu của bạn đã cố gắng để làm. Mảng này được phân bổ trên stack, trong khi ban đầu nó được trên đống. – Jon

+0

Vâng, tôi đoán vậy. Bây giờ mảng là một con trỏ và có con trỏ trong mảng. – Deukalion

0

Xác định một mảng con trỏ, kiểu con trỏ là BaseClass. Và gán con trỏ cho lớp thừa kế cho các phần tử của mảng. giống như:

BaseClass* base [2]; 
base[0] = new FirstDerivedClass; 
base[1] = new SecondDerivedClass;