如何将子类作为期望基类的函数的参数传递,然后将该对象传递给指向这些抽象类对象的指针向量?

Posted

技术标签:

【中文标题】如何将子类作为期望基类的函数的参数传递,然后将该对象传递给指向这些抽象类对象的指针向量?【英文标题】:how to pass subclass as parameter for function expecting base class then pass that object into vector of pointers to those abstract class objects? 【发布时间】:2020-01-03 02:59:19 【问题描述】:

TL;DR

我正在尝试将子类传递给期望子类的基类的函数,然后将该基类的唯一指针存储在第三个完全独立的类中的向量中。

(C++ 11 及更高版本)

结束 TL;DR

我总共有 3 节课,然后是我的 int main()

基(抽象)类有一个构造函数和一个虚函数。 基类构造函数实现了,虚函数没有实现。

第二个类是基类的子类。 它实现自己的构造函数并调用基本构造函数。 子类的第二部分是虚基类功能的具体实现。

然后我有了第三个类,它有自己的构造函数。 第三个类有一个函数,其函数头包含对基类的引用。 然后,这个相同的函数尝试将此引用传递给抽象类,然后将.push_back() 引用传递给此抽象类的std::unique_ptr 的向量。 (因为我不能直接拥有抽象类实例的向量。)

我的问题是我目前无法编译此代码的版本。

我一直在参考一些在线资源来尝试解决我的问题。

pass unique_ptr as an object
https://***.com/questions/8114276/how-do-i-pass-a-unique-ptr-argument-to-a-constructor-or-a-function

adding elements of a vector of a base class
https://***.com/questions/31410858/adding-elements-to-stdvector-of-an-abstract-class

can't access derived class method from pointer of base class - not entirely relavent, but good knowledge
https://***.com/questions/23489554/cant-access-derived-class-method-from-pointer-of-type-base-class

我在一个无法编译的示例 C++ 可执行文件中创建了这个问题的简化版本。

这是文件:


/*
This script demonstrates my dilemma of trying to pass a subclass object
as a parameter to a function that expects the base class, and then 
take that passed in object in the function and add it to a vector of that object 
in a completely different class.
*/

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

class Baseclass

    public:
        Baseclass(int i)
        
            lala = i;
        

    // subclass MUST implement this method.
    virtual int addme(int d) = 0;

    protected:
        int lala;
;

class Subclass : Baseclass

    public:
        Subclass(int d, int l) : Baseclass(d)
        
            blahblah = l;
        

        int addme(int d)
        
            return blahblah + lala + d;
        

    protected:
        int blahblah;
;

class Mainclass

    public:
        Mainclass(uint blah)
        
            anotherone = blah;
        

        // This is the function I cannot seem to get to work.
        // How can I make the function parameter an abstract class?
        // The object being passed in is NOT abstract...
        bool addController(Baseclass & basecont)
        
            // This line here does NOT compile!!
            // controllers.push_back(std::make_unique<What goes here?>(basecont));
            return true;
        

    protected:
        uint anotherone;
        std::vector<std::unique_ptr<Baseclass>> controllers;
;

int main(int argc , char ** argv)

    // create subclassed controllers
    Subclass cont1 = Subclass(12, 23);
    Subclass cont2 = Subclass(233, 2);
    // create main object
    Mainclass mainbro = Mainclass(23);
    // Add the subclased controllers to the main class
    // THESE 2 lines do not compile!!
    // mainbro.addController(cont1);
    // mainbro.addController(cont2);
    //
    return 0;


我认为我做错了什么,但我不觉得我概述的过程本身是不可能的。我只是认为我解决问题的方法是错误的。

我在脚本中突出显示了我不确定应该做什么以及代码中断的地方。

我可能需要采取替代方法来解决问题,我只是不知道我有哪些替代方法。

【问题讨论】:

使用bool addController(std::unique_ptr&lt;Baseclass&gt; basecount) 并将make_unique 对象传递给它。 (顺便说一句,很好的问题。) 错误信息是什么?你能专注于一个错误而不是(至少)两个错误吗? 【参考方案1】:

我看到了修复代码的不同方法,具有不同的含义。

存储指针(main 拥有对象的所有权)

class Mainclass

public:

    void addController(Baseclass& basecont)
    
        controllers.push_back(&basecont);
    

protected:
    std::vector<Baseclass*> controllers;
;

转让所有权

class Mainclass

public:

    void addController(std::unique_ptr<Baseclass> basecont)
    
        controllers.push_back(std::move(basecont));
    

protected:
    std::vector<std::unique_ptr<Baseclass>> controllers;
;

main:

int main()

    auto cont1 = std::make_unique<Subclass>(12, 23);
    auto cont2 = std::make_unique<Subclass>(233, 2);
    Mainclass mainbro(23);

    mainbro.addController(std::move(cont1));
    mainbro.addController(std::move(cont2));

存储副本

class Mainclass

public:

    void addController(Baseclass& basecont)
    
        controllers.push_back(basecont.clone());
    

protected:
    std::vector<std::unique_ptr<Baseclass>> controllers;
;

class Baseclass

// ...
public:
    virtual int addme(int d) = 0;
    virtual std::unique_ptr<Baseclass> clone() = 0;
;

class Subclass : Baseclass

// ...
public:
    std::unique_ptr<Baseclass> clone() override  return std::make_unique<Subclass>(*this); 
;

【讨论】:

【参考方案2】:

每当您将基指针或引用与虚方法一起使用时,总是添加一个虚析构函数:

    virtual ~Baseclass() = default;

这可以防止基指针被删除时出现未定义的行为。

接下来,使用公共继承来允许编译器从unique_ptr&lt;Subclass&gt; 隐式向上转换为unique_ptr&lt;Baseclass&gt;

class Subclass : public Baseclass

您的最后一个问题是所有权问题。通过拥有unique_ptr 的向量,您是在说您的类拥有所有这些对象。但是通过在main 的堆栈上声明它们,您就是说main 拥有它们。相反,在主例程中使用make_unique,并转让所有权std::move

        bool addController(std::unique_ptr<Baseclass> basecont)
        
            controllers.push_back(std::move(basecont));
            return true;
        

...

    auto cont1 = std::make_unique<Subclass>(12, 23);
    auto cont2 = std::make_unique<Subclass>(233, 2);
    // create main object
    Mainclass mainbro = Mainclass(23);
    mainbro.addController(std::move(cont1));
    mainbro.addController(std::move(cont2));

大家一起:

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

class Baseclass

    public:
        Baseclass(int i)
        
            lala = i;
        

    virtual ~Baseclass() = default;

    // subclass MUST implement this method.
    virtual int addme(int d) = 0;

    protected:
        int lala;
;

class Subclass : public Baseclass

    public:
        Subclass(int d, int l) : Baseclass(d)
        
            blahblah = l;
        

        int addme(int d)
        
            return blahblah + lala + d;
        

    protected:
        int blahblah;
;

class Mainclass

    public:
        Mainclass(uint blah)
        
            anotherone = blah;
        

        bool addController(std::unique_ptr<Baseclass> basecont)
        
            controllers.push_back(std::move(basecont));
            return true;
        

    protected:
        uint anotherone;
        std::vector<std::unique_ptr<Baseclass>> controllers;
;

int main(int argc , char ** argv)

    // create subclassed controllers
    auto cont1 = std::make_unique<Subclass>(12, 23);
    auto cont2 = std::make_unique<Subclass>(233, 2);
    // create main object
    Mainclass mainbro = Mainclass(23);
    mainbro.addController(std::move(cont1));
    mainbro.addController(std::move(cont2));
    return 0;

演示:https://godbolt.org/z/EyQD6S

【讨论】:

只是附带一件事:解析继承的类对象的类型是在编译时还是运行时发生的?我想知道上述方法是否会严重影响性能(大于 1~5 毫秒)。换句话说,当我有一个类方法调用存储在unique_ptr类型向量中的对象上的方法时,会不会需要很长时间来计算。 virtual 成员在设计上是在运行时调度的(请参阅:vtable)。但是,开销可以忽略不计,与使用函数指针大致相同。在强大的现代处理器上,如果您经常调用虚拟方法,指令缓存可能会完全消除这种微不足道的开销。【参考方案3】:
#include <iostream>
#include <memory>
#include <vector>

class Baseclass

    public:
        Baseclass(int i)
        
            lala = i;
        

    // subclass MUST implement this method.
    virtual int addme(int d) = 0;

    protected:
        int lala;
;

class Subclass : public Baseclass

    public:
        Subclass(int d, int l) : Baseclass(d)
        
            blahblah = l;
        

        int addme(int d)
        
            return blahblah + lala + d;
        

    protected:
        int blahblah;
;

class Mainclass

    public:
        Mainclass(uint blah)
        
            anotherone = blah;
        

        // you need to make the function a template, otherwise 
        // you'll slice the top off the SubClass, and incorrectly
        // make a copy of the base class (which you can't do, 
        // because BaseClass is pure virtual)
        template<typename T>
        bool addController(T& basecont)
        
            // dont push_back new unique_ptrs, emplace_back instead!
            controllers.emplace_back(new T(basecont));
            return true;
        

    protected:
        uint anotherone;
        std::vector<std::unique_ptr<Baseclass>> controllers;
;

int main(int argc , char ** argv)

    // create subclassed controllers
    Subclass cont1 = Subclass(12, 23);
    Subclass cont2 = Subclass(233, 2);
    // create main object
    Mainclass mainbro = Mainclass(23);

    // It's worth pointing out that these methods will take new copies of 
    // cont1 and cont2 (we don't want the mainbro instance to delete the 
    // memory for cont1 and cont2, since they are stack allocated)
    mainbro.addController(cont1);
    mainbro.addController(cont2);
    //
    return 0;

【讨论】:

以上是关于如何将子类作为期望基类的函数的参数传递,然后将该对象传递给指向这些抽象类对象的指针向量?的主要内容,如果未能解决你的问题,请参考以下文章

在子类中使用父类的对象。您需要将对象作为类构造函数传递吗?

如何将基类的未知子类放在一个数据结构中并在 C++ 中调用重写的基类函数

是否可以通过引用以基类作为参数的函数来传递派生类

给定一个基类作为参数,如果传递了派生类,我如何使用 op<< 重载来打印派生类的特征?

C++中,继承时,创建子类对象,能否在子类构造函数初始化列表里调用基类构造函数?

基类和子类函数名相同且参数类型一样,基类函数如何被覆盖,还存在吗?存在何处?请详细回答,谢谢~~~~