2012-04-14 39 views
6
đang

tham khảo:C++ vector với thừa kế

#include <vector> 
#include <iostream> 

class Func { 
public: 
    virtual void call() { 
     std::cout<< "Func -> call()" << std::endl; 
    } 
}; 

class Foo : public Func { 
public: 
    void call() { 
     std::cout<< "Foo -> call()" << std::endl; 
    } 
}; 

class Bar : public Func { 
public: 
    void call() { 
     std::cout<< "Bar -> call()" << std::endl; 
    } 
}; 

int main(int argc, char** argv) { 
    std::vector<Func> functors; 

    functors.push_back(Func()); 
    functors.push_back(Foo()); 
    functors.push_back(Bar()); 

    std::vector<Func>::iterator iter; 
    for (iter = functors.begin(); iter != functors.end(); ++iter) 
     (*iter).call(); 
} 

Khi chạy mã đó, nó tạo ra đầu ra sau trên máy tính của tôi:

$ ./test 
Func -> call() 
Func -> call() 
Func -> call() 

Nên có cách nào để đảm bảo rằng các chức năng ảo đúng được gọi trong trường hợp này? Tôi mới ở C++ nhưng dự đoán tốt nhất của tôi là ở đây:

(*iter).call(); 

Nó đang được truyền tới đối tượng Func. Điều này có đúng không?

+0

có thể trùng lặp của [Lưu trữ hai lớp với cùng một lớp cơ sở trong một std :: vector] (http://stackoverflow.com/questions/8777724/store-two-classes-with-the-same-base-class -in-a-stdvector) –

+0

vâng, tuy nhiên tôi không thể tìm thấy bài đăng đó trước khi tôi tạo cái này, xin lỗi tìm kiếm-fu của tôi không được tốt như bạn ạ –

+0

Có đối tượng cắt xảy ra trong trường hợp của bạn, Vì vậy phần bắt nguồn được cắt bỏ. Bạn nên sử dụng con trỏ của lớp dựa trên cho lưu trữ đối tượng dị. – Naveen

Trả lời

6

Bạn nên sử dụng shared_ptr hoặc unique_ptr để giữ các phần tử của tập hợp loại đa hình.

Khi mã của bạn được viết bây giờ các trường hợp Foo và Bar được cooerced (bản sao xây dựng) vào một thể hiện của loại Func để phù hợp với vectơ. (Lý do là vector lưu trữ các phần tử của nó ngay lập tức bằng giá trị có kích thước cố định cho hiệu suất, tuy nhiên các lớp con đa hình có kích thước lớn hơn không xác định tại thời gian biên dịch, vì vậy nó chỉ có thể lưu trữ lớp cơ sở.)

:

int main(int argc, char** argv) { 
    vector<shared_ptr<Func>> functors; 

    functors.push_back(make_shared<Func>()); 
    functors.push_back(make_shared<Foo>()); 
    functors.push_back(make_shared<Bar>()); 

    for (auto functor : functors) 
     functor->call(); 
} 

Ở trên, con trỏ được tham chiếu được sử dụng để chia sẻ hoàn toàn các lớp con hetrogeneous của Func trong vectơ. (Sự gián tiếp này cho phép các lớp con có kích thước tùy ý của Func được lưu trữ bằng cách chỉ định địa chỉ.)

Ngoài ra, bạn có thể xem std :: function và std :: bind, chứ không phải là kiểu functor của riêng bạn.

Một điều khác cần xem xét là mẫu chuyển tiếp và biến thể hoàn hảo.

cập nhật: Đối với trình biên dịch cũ:

int main(int argc, char** argv) { 
    vector<std::tr1::shared_ptr<Func> > functors; 

    functors.push_back(std::tr1::make_shared<Func>()); 
    functors.push_back(std::tr1::make_shared<Foo>()); 
    functors.push_back(std::tr1::make_shared<Bar>()); 

    for (size_t i = 0; i < functors.size(); ++i) 
     functors[i]->call(); 
} 
+0

+1 cho đề xuất để xem xét std :: chức năng và ràng buộc – lurscher

+0

Tôi không thể có vẻ để có được mã này để biên dịch, là mã tăng này hoặc std c + +? trình biên dịch của tôi: 'i686-apple-darwin11-llvm-g ++ - 4.2 (GCC) 4.2.1 (Dựa trên Apple Inc. xây dựng 5658) (LLVM build 2336.9.00) Apple clang phiên bản 3.1 (thẻ/Apple /clang-318.0.58) (dựa trên LLVM 3.1svn) ' –

+0

@RonElliott: Xin lỗi đoạn mã trên đang sử dụng chuẩn C++ hiện tại (2011). Tôi sẽ viết một phiên bản với tiêu chuẩn cũ, treo trên. –

1

std :: vector của bạn được lưu trữ đối tượng Func - điều này có nghĩa là khi bạn gọi

functors.push_back(Foo()); 
functors.push_back(Bar()); 

bạn đang tạo FooBar đối tượng , sau đó "cắt" các đối tượng đó khi chúng được sao chép thành các đối tượng Func.

Nếu bạn muốn sử dụng Foo và Bar polymorphically, sau đó một mô hình điển hình hơn sẽ được lưu trữ một vector của một số loại con trỏ (Tốt không "thô" con trỏ mặc dù), ví dụ

std::vector< std::unique_ptr<Func> >

std::vector< std::shared_ptr<Func> >

Hoặc, nếu bạn thực sự phải ..(Nhưng chỉ khi bạn đang sử dụng một trình biên dịch cũ mà không có shared_ptr hoặc unique_ptr)

std::vector< Func* >

2

vector chỉ nắm giữ loại Func theo giá trị, có nghĩa là tất cả temporals bạn FooBar được thái lát và Casted đến cơ sở Func loại

bạn cần thay đổi một cái gì đó giống như std::vector< Func* > và tự động phân bổ các lớp trường hợp có nguồn gốc để cho văn đa hình làm việc

Nếu yo u là hoàn toàn chắc chắn rằng bạn sẽ không vượt qua vector này để các chức năng khác sau khi chức năng này trở lại, như là một tối ưu hóa bạn có thể muốn phân bổ các trường hợp trong ngăn xếp:

std::vector< Func* > v; 
Bar b; 
Foo f; 
v.push_back(&b); 
v.push_back(&f); 
+0

Điều này đang hoạt động, vâng tôi có thể chắc chắn rằng vectơ này sẽ không được thông qua xung quanh vì vậy đó là cách tôi dự định triển khai –

1

Trong C++ đa hình chỉ làm việc với con trỏ và tham khảo , trong khi các vector lưu trữ trực tiếp các thể hiện của các đối tượng. Khi bạn gọi push_back, hàm tạo bản sao của Func được gọi, tạo đối tượng Func được lưu trữ bên trong vectơ.

Điều này được gọi là cắt đối tượng, bạn có thể tìm hiểu thêm về nó với tìm kiếm nhanh trong StackOverflow.

Giải pháp sẽ là lưu trữ con trỏ (hoặc, tốt hơn, thông minh con trỏ) cho đối tượng của bạn, cần được phân bổ ở nơi khác (có thể trên heap).

0

Vấn đề của bạn là bạn có vectơ là Func, nhưng phương pháp được gọi là đa hình chỉ thông qua tài liệu tham khảo hoặc con trỏ.

1

Nói chung, các thể hiện của các lớp con có thể lớn hơn các lớp con của chúng, vì vậy bạn không nên mong đợi các lớp con phù hợp với vị trí của vectơ của bạn.

push_back lẽ sẽ gọi trong nội bộ một constructor sao chép (của lớp Func, kể từ khi bạn có vector<Func>) để các khe vector nội bộ có thực sự Func không của một số lớp khác.