std容器中的抽象类 [英] abstract classes in std containers

查看:132
本文介绍了std容器中的抽象类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

很多时候,当我编程时,我使用多态性,因为它自然地建模我需要的对象。另一方面,我经常使用标准容器来存储这些对象,我倾向于避免指针,因为这要么需要释放对象,而不是将它们从堆栈中弹出,或者要求我确保对象将保持在堆栈,而我使用指针。当然有各种各样的指针容器对象,为你做这个任务,但在我的经验,他们也不是理想的,甚至恼人。那是;如果这样一个简单的解决方案存在,它会是在c ++语言,对吧? ;)



所以让我们有一个经典的例子:

  #include< iostream> 
#include< vector>

struct foo {};
struct goo:public foo {};
struct moo:public foo {};

int main(){
std :: vector< foo> foos;
foos.push_back(moo());
foos.push_back(goo());
foos.push_back(goo());
foos.push_back(moo());

return 0;
}

请参阅: http://ideone.com/aEVoSi 。这工作正常,如果对象有不同的sizeof的编译器可以应用切片。但是,由于c ++不知道像Java这样的实例,并且根据我的知识,没有足够的替代存在,在从向量中提取它们之后,不能访问继承类的属性。



因此,将使用虚函数,但是这不允许分配一个foo,因此不允许在向量中使用它们。请参见为什么我们不能声明std :: vector< AbstractClass> ;?



例如,我可能想要打印两个子类,简单的功能,是吗?

  #include< iostream> 
#include< vector>

struct foo {
virtual void print()= 0;
virtual〜foo(){}
};

struct goo:public foo {
int a;
void print(){std :: cout<< goo; }
};

struct moo:public foo {
int a,b;
void print(){std :: cout<< moo; }
};

int main(){
std :: vector< foo> foos;
foos.push_back(moo());
foos.push_back(goo());
foos.push_back(goo());
foos.push_back(moo());

for(foo& f:foos){
f.print();
}
return 0;
}

资料来源: http://ideone.com/I4rYn9



这是一个简单的补充,作为一个设计师,我永远不会想这种行为在前瞻。我已经非常兴奋的是,c ++能够切割我的对象,因此存储不同大小的对象在一个向量。不幸的是它不能这样做,当基类是抽象的,如下所述:为什么我们声明一个std :: vector< AbstractClass>?



一般的好解决方案似乎是使用指针。但是这(1)强迫我做内存管理和(2)我需要改变接口和重新编码很多东西。例如,考虑我首先有一个类接口返回一个std :: vector< foo>,现在它返回一个std :: vector< foo *>,所以我需要检查和更改foo的所有调用;



因此,基本上,imho是一个小功能添加 >大代码后果



我的问题是wrt编码标准。如何防止这些烦恼发生?我应该总是使用指针,并做我所有的内存管理?

基于40two的答案我做了这个小窍门:

  #include< iostream> 
#include< vector>
#include< memory>

struct foo {
virtual void print()= 0;
};

struct goo:public foo {
int a;
void print(){std :: cout<< goo; }
};

struct moo:public foo {
int a,b;
void print(){std :: cout<< moo; }
};
typedef std :: unique_ptr< foo> foo_ptr;
int main(){
std :: vector< std :: unique_ptr< foo> > foos;
foos.push_back(foo_ptr(new moo));
foos.push_back(foo_ptr(new goo));
foos.push_back(foo_ptr(new goo));
foos.push_back(foo_ptr(new moo));

for(auto it = foos.begin(); it!= foos.end(); ++ it){
it-> get() - & ;
}
return 0;
}

资料来源: http://ideone.com/ym4SY2

解决方案

支持C ++ 11功能,将使用 std :: vector< std :: shared_ptr< foo>> std :: vector< std :: unique_ptr< foo>> 而不是原始指针,如下例所示:

  include< iostream> 
#include< memory>
#include< vector>

struct foo {
virtual void print()= 0;
};

struct goo:public foo {
int a;
void print(){std :: cout<< goo; }
};

struct moo:public foo {
int a,b;
void print(){std :: cout<< moo; }
};

auto main() - > int {
std :: vector< std :: shared_ptr< foo>> v {std :: make_shared< goo>(),std :: make_shared< moo>()};
for(auto i:v){
i-> print();
std :: cout<< std :: endl;
}
return 0;
}

std :: vector< std :: unique_ptr< foo>>

  auto main() int {
std :: vector< std :: unique_ptr< foo>> v;
v.push_back(std :: move(std :: unique_ptr< goo>(new goo))));
v.push_back(std :: move(std :: unique_ptr< moo>(new moo))));
for(auto it(v.begin()),ite(v.end()); it!= ite; ++ it){
(* it) - > print
std :: cout<< std :: endl;
}
return 0;
}

因此,您不必担心内存释放。


Very often, when I program, I use polymorphism because it naturally models the objects that I need. On the other hand I very often use standard containers to store these objects, and I tend to avoid pointers because this either requires me to free up the objects instead of popping them off the stack or requires me to know for sure the objects will stay on the stack while I use the pointer. Of course there are all kinds of pointer-container objects that sort of do this task for you, but in my experience they are also not ideal or even annoying. That is; if such a simple solution existed, it would have been in the c++ language, right ? ;)

So lets have a classic example:

#include <iostream>
#include <vector>

struct foo {};
struct goo : public foo {};
struct moo : public foo {};

int main() {
    std::vector<foo> foos;
    foos.push_back(moo());
    foos.push_back(goo());
    foos.push_back(goo());
    foos.push_back(moo());

    return 0;
}

See: http://ideone.com/aEVoSi . This works fine, and if the objects have different sizeof's the compiler may apply slicing. However, due to the the fact that c++ knows no instanceof like Java, and to the best of my knowledge no adequate alternative exists, one cannot access the properties of the inherited classes after fetching them as a foo from the vector.

Hence one would use virtual function, however this disallows one to allocate a foo, and hence one is not permitted to use them in a vector. See Why can't we declare a std::vector<AbstractClass>? .

For example I may want to be able to print both subclasses, simple feature, right?

#include <iostream>
#include <vector>

struct foo {
        virtual void print() =0;
        virtual ~foo() {}
};

struct goo : public foo {
    int a;
    void print() { std::cout << "goo"; }
};

struct moo : public foo {
    int a,b;
    void print() { std::cout << "moo"; }
};

int main() {
    std::vector<foo> foos;
    foos.push_back(moo());
    foos.push_back(goo());
    foos.push_back(goo());
    foos.push_back(moo());

    for(foo& f : foos) {
        f.print();
    }
    return 0;
}

Source: http://ideone.com/I4rYn9

This is a simple addition, as a designer I would never think of wanting this behavior in foresight. I would already be so thrilled by the fact that c++ was able to slice my objects and hence store objects of different sizes in one vector. Unfortunately it cannot do so anymore when the base class is abstract, as stated here: Why can't we declare a std::vector<AbstractClass>?

The general good solution seems to be to use pointers. But this (1) forces me to do memory management and (2) I'd need to change interfaces and recode a lot of things. For instance, consider that I first had some class interface returning a std::vector<foo>, now it returns a std::vector<foo *>, so I need to check and change all the calls of foo; which is annoying, or even impossible if I am writing a library.

So basically, imho, that is a small feature addition with big code consequences.

My question is w.r.t. coding standards. How can I prevent that these annoyances occur? Should I always use pointers, and do all my memory management? Should I always assume a class might become abstract along the way?

EDIT, ANSWER: Based on the answer of 40two I made this sniplet:

#include <iostream>
#include <vector>
#include <memory>

struct foo {
    virtual void print() =0;
};

struct goo : public foo {
    int a;
    void print() { std::cout << "goo"; }
};

struct moo : public foo {
    int a,b;
    void print() { std::cout << "moo"; }
};
typedef std::unique_ptr<foo> foo_ptr;
int main() {
    std::vector<std::unique_ptr<foo> > foos;
    foos.push_back(foo_ptr(new moo));
    foos.push_back(foo_ptr(new goo));
    foos.push_back(foo_ptr(new goo));
    foos.push_back(foo_ptr(new moo));

    for(auto it = foos.begin(); it!=foos.end(); ++it) {
        it->get()->print();
    }
    return 0;
}

Source: http://ideone.com/ym4SY2

解决方案

One solution if you compiler supports C++11 features, would be to use std::vector<std::shared_ptr<foo>> or std::vector<std::unique_ptr<foo>>instead of raw pointers like the example below:

#include <iostream>
#include <memory>
#include <vector>

struct foo {
    virtual void print() = 0;
};

struct goo : public foo {
    int a;
    void print() { std::cout << "goo"; }
};

struct moo : public foo {
    int a,b;
    void print() { std::cout << "moo"; }
};

auto main() -> int {
    std::vector<std::shared_ptr<foo>> v{std::make_shared<goo>(), std::make_shared<moo>()};
    for(auto i : v) { 
        i->print();
        std::cout << std::endl;
    }
    return 0;
}

or with std::vector<std::unique_ptr<foo>>:

auto main() -> int {
    std::vector<std::unique_ptr<foo>> v;
    v.push_back(std::move(std::unique_ptr<goo>(new goo)));
    v.push_back(std::move(std::unique_ptr<moo>(new moo)));
    for(auto it(v.begin()), ite(v.end()); it != ite; ++it) { 
        (*it)->print();
        std::cout << std::endl;
    }
    return 0;
}

Thus, you wouldn't have to worry about memory deallocation.

这篇关于std容器中的抽象类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆