2008-09-22 29 views
22

Hãy xem xét các lớp này.Nhận véc tơ <Derived*> vào một chức năng mong đợi một vector <Base*>

class Base 
{ 
    ... 
}; 

class Derived : public Base 
{ 
    ... 
}; 

chức năng này

void BaseFoo(std::vector<Base*>vec) 
{ 
    ... 
} 

Và cuối cùng vector của tôi

std::vector<Derived*>derived; 

Tôi muốn vượt qua derived hoạt BaseFoo, nhưng trình biên dịch không cho tôi. Làm cách nào để giải quyết vấn đề này mà không cần sao chép toàn bộ vectơ vào một số std::vector<Base*>?

+0

Tôi nghĩ bạn cần làm rõ liệu BaseFoo có dự định lấy véc-tơ bằng tham chiếu const hay không const (sẽ rất khác thường nếu nó lấy giá trị theo giá trị). Nói cách khác, BaseFoo có cần sửa đổi véc-tơ không? –

Trả lời

41

vector<Base*>vector<Derived*> là các loại không liên quan, vì vậy bạn không thể thực hiện việc này. Điều này được giải thích trong C++ FAQ here.

Bạn có thể thay đổi biến của mình từ một số vector<Derived*> thành vector<Base*> và chèn Derived đối tượng vào đó.

Ngoài ra, bạn phải vượt qua các vector bởi const tham khảo, không phải bởi giá trị:

void BaseFoo(const std::vector<Base*>& vec) 
{ 
    ... 
} 

Cuối cùng, để tránh rò rỉ bộ nhớ, và làm cho mã của bạn ngoại lệ an toàn, hãy xem xét sử dụng một container được thiết kế để xử lý đống đối tượng -allocated, ví dụ:

#include <boost/ptr_container/ptr_vector.hpp> 
boost::ptr_vector<Base> vec; 

Ngoài ra, thay đổi véc tơ để giữ một con trỏ thông minh thay vì sử dụng con trỏ liệu:

#include <memory> 
std::vector< std::shared_ptr<Base*> > vec; 

hoặc

#include <boost/shared_ptr.hpp> 
std::vector< boost::shared_ptr<Base*> > vec; 

Trong mỗi trường hợp, bạn sẽ cần phải sửa đổi chức năng BaseFoo của bạn cho phù hợp.

+3

+1: Câu trả lời đầy đủ và chính xác. – ereOn

8

một lựa chọn là sử dụng một mẫu

template<typename T> 
void BaseFoo(const std::vector<T*>& vec) 
{ 
... 
} 

Nhược điểm là việc thực hiện phải là trong tiêu đề và bạn sẽ nhận được một chút mã sưng lên. Bạn sẽ kết thúc với các chức năng khác nhau được khởi tạo cho từng loại, nhưng mã vẫn giữ nguyên. Tùy thuộc vào trường hợp sử dụng nó là một giải pháp nhanh chóng và bẩn.

Chỉnh sửa, tôi cần lưu ý lý do chúng tôi cần mẫu ở đây là vì chúng tôi đang cố gắng viết cùng một mã cho các loại không liên quan như được một số áp phích khác lưu ý. Mẫu cho phép bạn giải quyết các vấn đề chính xác này. Tôi cũng đã cập nhật nó để sử dụng tham chiếu const. Bạn cũng nên truyền các đối tượng "nặng" như một vec-tơ bởi tham chiếu const khi bạn không cần một bản sao, mà về cơ bản là luôn luôn.

+0

Trình biên dịch có khả năng được phép tối ưu hóa hai cá thể của hàm thành một chỉ bằng cách nhận ra mã đối tượng được tạo là chính xác như nhau. Doubt nhiều người sẽ làm như vậy với một chức năng phức tạp mặc dù. –

+0

Thú vị, tôi đã thấy trình liên kết vứt bỏ mã dự phòng. –

+0

Bạn có thể tránh sự cần thiết phải xác định chức năng trong tập tin tiêu đề bằng cách sử dụng instantiations rõ ràng. Bạn có thể hợp nhất ví dụ của tôi (bên dưới) vào câu trả lời của bạn. –

0

Chúng là các loại không liên quan - bạn không thể.

2

Nói chung, bạn sẽ bắt đầu với một vùng chứa con trỏ cơ sở, không phải theo cách khác.

+0

Tôi nghĩ cả hai trường hợp đều xảy ra trong thực tế. Nhưng tôi sẽ đồng ý một vector là phổ biến hơn trong thiết kế OO tốt. –

1

Nếu bạn làm việc với một thư viện của bên thứ ba, và đây là hy vọng duy nhất của bạn, sau đó bạn có thể làm điều này:

BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived)); 

Nếu không sửa chữa mã của bạn với một trong những suggesstions khác.

+1

Trong khi đó thực sự có thể hoạt động, nó giống như một quả bom thời gian. Tôi rất do dự khi sử dụng reinterpret_cast trừ khi tôi biết chính xác những gì tôi đang làm. –

+0

Chỉ cần không làm điều đó, nó không phải là garanteed để làm việc. –

+1

Đây là câu trả lời duy nhất cho câu hỏi. Các câu trả lời khác về cơ bản là "tránh vấn đề đó" - đây là những lời khuyên tốt, nhưng không phải lúc nào cũng áp dụng được. Nó có thể đơn giản hơn một chút: BaseFoo (reinterpret_cast &> (có nguồn gốc)); – 0xF

22

Thay vì chuyển đối tượng vùng chứa (vector<>), hãy chuyển qua số beginend trình lặp như phần còn lại của thuật toán STL. Chức năng nhận chúng sẽ được tạo khuôn mẫu, và nó sẽ không thành vấn đề nếu bạn truyền vào Derived * hoặc Base *.

+2

+1: điều dễ nhất để làm mà không cần tăng hoặc chia sẻ con trỏ (mà tôi chưa biết) – nkint

1

Nếu std::vector hỗ trợ những gì bạn đang yêu cầu, sau đó nó sẽ có thể để đánh bại các hệ thống kiểu C++ mà không sử dụng bất kỳ phôi (chỉnh sửa: liên kết ChrisN để các cuộc đàm phán C++ FAQ Lite về cùng một vấn đề):

class Base {}; 
class Derived1 : public Base {}; 
class Derived2 : public Base {}; 

void pushStuff(std::vector<Base*>& vec) { 
    vec.push_back(new Derived2); 
    vec.push_back(new Base); 
} 

... 
std::vector<Derived1*> vec; 
pushStuff(vec); // Not legal 
// Now vec contains a Derived2 and a Base! 

Vì hàm BaseFoo() của bạn lấy véc-tơ theo giá trị, nó không thể sửa đổi véc tơ gốc mà bạn đã chuyển vào, vì vậy những gì tôi viết sẽ không thể thực hiện được. Nhưng nếu tham chiếu không phải const và bạn sử dụng reinterpret_cast<std::vector<Base*>&>() để chuyển số std::vector<Derived*>, bạn có thể không nhận được kết quả mong muốn và chương trình của bạn có thể gặp sự cố.

Hỗ trợ mảng Java covariant subtyping và điều này yêu cầu Java phải do a runtime type check every time you store a value in an array. Điều này cũng là không mong muốn.

2

Lấy Matt Price's câu trả lời từ phía trên, cho rằng bạn biết trước những gì loại bạn muốn sử dụng với chức năng của bạn, bạn có thể khai báo hàm template trong file header, và sau đó thêm instantiations rõ ràng đối với những loại:

// BaseFoo.h 
template<typename T> 
void BaseFoo(const std::vector<T*>& vec); 

// BaseFoo.cpp 
template<typename T> 
void BaseFoo(const std::vector<T*>& vec); 
{ 
... 
} 

// Explicit instantiation means no need for definition in the header file. 
template void BaseFoo<Base> (const std::vector<Base*>& vec); 
template void BaseFoo<Derived> (const std::vector<Derived*>& vec); 
12

Sự cố này xảy ra ở các ngôn ngữ lập trình có vùng chứa có thể thay đổi. Bạn không thể đi qua một túi táo có thể thay đổi được như một túi trái cây bởi vì bạn không thể chắc chắn rằng một người nào khác không đặt một quả chanh vào túi trái cây đó, sau đó nó không còn đủ điều kiện như một túi táo nữa. Nếu túi táo không thể thay đổi được, thì bỏ qua nó như một túi trái cây sẽ ổn thôi. Tìm kiếm hiệp phương sai/contravariance.

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