简体   繁体   English

将派生的 class 对象存储在基础 class 变量中

[英]Store derived class objects in base class variables

I would like to store instances of several classes in a vector.我想将几个类的实例存储在一个向量中。 Since all classes inherit from the same base class this should be possible.由于所有类都从相同的基础 class 继承,这应该是可能的。

Imagine this program:想象一下这个程序:

#include <iostream>
#include <vector>
using namespace std;

class Base
{
    public:
    virtual void identify ()
    {
        cout << "BASE" << endl;
    }
};

class Derived: public Base
{
    public:
    virtual void identify ()
    {
        cout << "DERIVED" << endl;
    }
};

int main ()
{
    Derived derived;
    
    vector<Base> vect;
    vect.push_back(derived);
    
    vect[0].identify();
    return 0;
}

I expected it to print "DERIVED" , because the identify() method is virtual .我希望它打印"DERIVED" ,因为identify()方法是virtual Instead vect[0] seems to be a Base instance and it prints "BASE" .相反vect[0]似乎是一个Base实例,它打印"BASE"

I guess I could write my own container (probably derived from vector ) somehow that is capable of doing this (maybe holding only pointers...).我想我可以以某种方式编写自己的容器(可能源自vector ),它能够做到这一点(也许只保存指针......)。

I just wanted to ask if there is a more C++'ish way to do this.我只是想问一下是否有更多的 C++'ish 方式来做到这一点。 AND I would like to be completely vector -compatible (just for convenience if other users should ever use my code).而且我想完全兼容vector (如果其他用户应该使用我的代码,只是为了方便)。

What you are seeing is Object Slicing .您所看到的是Object Slicing
You are storing object of Derived class in an vector which is supposed to store objects of Base class, this leads to Object slicing and the derived class specific members of the object being stored get sliced off, thus the object stored in the vector just acts as object of Base class.您将派生类的对象存储在一个向量中,该向量应该存储基类的对象,这会导致对象切片,并且正在存储的对象的派生类特定成员被切掉,因此存储在向量中的对象只是充当基类的对象。

Solution:解决方案:

You should store pointer to object of Base class in the vector:您应该在向量中存储指向基类对象的指针:

vector<Base*> 

By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well.通过存储指向 Base 类的指针,就不会进行切片,并且您也可以实现所需的多态行为。
Since you ask for a C++ish way of doing this, the right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector.由于您要求以C++ish方式执行此操作,因此正确的方法是使用合适的智能指针,而不是在向量中存储原始指针。 That will ensure you do not have to manually manage the memory, RAII will do that for you automatically.这将确保您不必手动管理内存, RAII会自动为您完成。

You're experiencing slicing.你正在经历切片。 The vector copies the derived object, a new one of type Base is inserted.向量复制derived对象,插入一个新的Base类型。

TL;DR: You should not inherit from a publicly copyable/movable class. TL;DR:您不应该从可公开复制/可移动的类继承。


It is actually possible to prevent object slicing, at compilation time: the base object should not be copyable in this context.实际上可以在编译时防止对象切片:在这种情况下,基础对象不应该是可复制的。

Case 1: an abstract base案例 1:抽象基础

If the base is abstract, then it cannot be instantiated and thus you cannot experience slicing.如果基础是抽象的,则无法实例化,因此您无法体验切片。

Case 2: a concrete base案例2:混凝土基础

If the base is not abstract, then it can be copied (by default).如果基础不是抽象的,那么它可以被复制(默认情况下)。 You have two choices:你有两个选择:

  • prevent copy altogether完全防止复制
  • allow copy only for children只允许儿童复制

Note: in C++11, the move operations cause the same issue.注意:在 C++11 中,移动操作会导致同样的问题。

// C++ 03, prevent copy
class Base {
public:

private:
    Base(Base const&);
    void operator=(Base const&);
};

// C++ 03, allow copy only for children
class Base {
public:

protected:
    Base(Base const& other) { ... }
    Base& operator=(Base const& other) { ...; return *this; }
};

// C++ 11, prevent copy & move
class Base {
public:
    Base(Base&&) = delete;
    Base(Base const&) = delete;
    Base& operator=(Base) = delete;
};

// C++ 11, allow copy & move only for children
class Base {
public:

protected:
    Base(Base&&) = default;
    Base(Base const&) = default;
    Base& operator=(Base) = default;
};

I'd use vector<Base*> to store them.我会使用vector<Base*>来存储它们。 If you say vector<Base> , slicing will occur.如果你说vector<Base> ,就会发生切片。

This does mean that you'd have to delete the actual objects yourself after you've removed the pointers from your vector, but otherwise you should be fine.这确实意味着在从向量中删除指针后,您必须自己删除实际对象,否则应该没问题。

// Below is the solution by using vector<Based*> vect,
// Base *pBase , and initialized pBase with
// with the address of derived which is
// of type Derived

#include <iostream>
#include <vector>

using namespace std;

class Base
{

public:

virtual void identify ()
{
    cout << "BASE" << endl;
}
};

class Derived: public Base
{
public:
virtual void identify ()
{
    cout << "DERIVED" << endl;
}
};

int main ()

{
Base *pBase; // The pointer pBase of type " pointer to Base"
Derived derived;
// PBase is initialized with the address of derived which is
// of type Derived

pBase = & derived;
// Store pointer to object of Base class in the vector:
vector<Base*> vect;
// Add an element to vect using pBase which is initialized with the address 
// of derived
vect.push_back(pBase);
vect[0]->identify();
return 0;
}

As all others mentioned here, you cannot accomplish to insert an object of derived to a vector of base due to object slicing that would happen on copy construction.正如这里提到的所有其他人一样,由于复制构造时会发生 object 切片,因此您无法将派生的 object 插入到碱基向量中。

If goal is to avoid memory allocation, you can use an std::variant , but the vector would no longer be of base class如果目标是避免 memory 分配,您可以使用std::variant ,但向量将不再是基本class

using HierarchyItem = std::variant<Base, Derived>;

int main()
{
    vector<HierarchyItem> vect;
    vect.push_back(Derived());

    std::visit([](auto &&hier_item){ hier_item.identify(); }, vect[0]);

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM