包含多态对象的向量:静态断言错误

问题描述 投票:1回答:2

从大学开始就不使用C ++,我试图使用带有两种子对象的向量,我显然遇到了错误。

最初我使用了一个指针向量,但是如果我理解正确,那么在清除时会泄漏内存。

我得到的错误让我相信它与类中的静态计数器有关(用最后一个成员销毁?),但删除它并没有解决它。

错误导致这里,在stl_construct.h中:

#if __cplusplus >= 201103L
      // A deleted destructor is trivial, this ensures we reject such types:
      static_assert(is_destructible<_Value_type>::value,
            "value type is destructible");
#endif

好吧,好吧,但我的析构函数都是明确声明的。我记得父类应该使用虚拟析构函数并修复它,但问题仍然是一样的。

在虚拟父类上将构造函数/析构函数移动到public不应该(实际上也不会)更改内容。

现在我假设我以某种方式滥用了这个向量。这是我的例子:

主要:

#include <stdio.h>
#include "Foo.h"
#include <iostream>
Bar buildBar();

int main(int argc, char **argv)
{
    std::vector<Foo> Foos;
    Foos.reserve(1);
    Foos.push_back(buildBar());
    for (int idx=(0);sizeof(Foos);idx++)
    {
        try {
            std::cout <<  "x = " << Foos.at(idx).getLoc().at(0);
        }
        catch (std::exception& e) {
                std::cout <<  idx << ": Index out of range" << std::endl;
            }
    }
Foos.clear();
return 0;
}

Bar buildBar()
{
    Bar* temp = new Bar(5,6);
    return *temp;
}

Foo.h,构造函数移动到标题:

#ifndef FOO_H
#define FOO_H
#include <vector>
class Foo
{
public:
    //int maxoID(){return lastoID;} //0-indexed
    /* Other things */
    virtual std::vector<int> getLoc(){ return {x_Base,y_Base};}
    Foo(): 
    //oID(-1), 
    x_Base(-1),
    y_Base(-1){}
    virtual ~Foo(){}
protected:
    int x_Base;
    int y_Base;
};

class Bar : public Foo
{
public:
    Bar(int x1, int y1):
        x_End(-1),
        y_End(-1)
        { 
            x_Base = x1; 
            y_Base = y1;
        }
    ~Bar(){}
    std::vector<int> getLoc() {return {x_Base,y_Base,x_End,y_End};}
protected:
    int x_End;
    int y_End;

};

#endif // FOO_H
c++ vector static-assert
2个回答
1
投票

首先,擅长质疑你对原始指针的使用!人们常常盲目地使用它们,最终会导致其他问题。

你目前的问题是你有object slicing。当你将Bar插入vector<Foo>时,你会失去关于Bar的重要信息。如果你调用一个只接受Foo而不是Foo&Foo*的函数,也会发生同样的情况。

根据您的使用情况,您可以使用std::unique_ptrstd::shared_ptrstd::reference_wrapper

请注意,您可以使用原始指针,它们不会自动泄漏内存,但vector将不负责内存(这是使用我指出的智能指针之一的美妙)

这是完全可以接受的:

int main()
{
    Foo* f = new Foo;
    {
        std::vector<Foo*> v;
        v.push_back(f);
    } // v goes out of scope and is cleaned up
    delete f; // the vector won't have cleaned this up, it's our responsibility    
}

使用unique_ptr只是让它变得更加复杂。当然,在这样一个轻量级示例中,原始指针很容易使用,但在较大的程序中,它可能会失控:

另请注意,作为@juanchopanza points out in the comments,您的buildBar函数会泄漏内存。你打电话的时候

return *temp;

您创建一个副本,您将丢失temp的内存地址,因此无法删除它。

Bar buildBar()
{
    Bar* temp = new Bar(5,6);
    return *temp;
}

Bar b = buildBar();
Bar* p = &b; // this only reference the copy, b
delete p; // this is bad, this will (double) delete the memory from b, which is not the same as temp.

b超出范围时会被自动清理(如果你也试图删除它,这是不好的),但是你没办法删除temp


0
投票

欢迎回到C ++!

您不能将派生类型放在基类型的向量中。实例只能是基本类型。如果您尝试将派生类型添加到此向量,它将仅复制派生类的基本部分。

你是第一次是对的 - 你需要一个基本指针向量。只要在从向量中删除指针时删除指针,这就不会泄漏。

但是,这是2017年,我们现在希望避免新的/删除。所以你可以使用unique_ptr(C ++ '11)的向量,当它们超出范围时自动删除内存。

std::vector<std::unique_ptr<Bar>> v;
v.push_back(std::make_unique<Bar>(0, 0));

您还可以使用变体向量(C ++'17),它们是新奇的类型安全联合。

typedef std::variant<Bar, Foo> MyBar
std::vector<MyBar> v;

在元素上使用std::visitstd::get来获取其类型。

编辑:这是从变体向量中提取元素的一些代码

bool apply(int& i)
{
    std::cout << "integer: " << i << std::endl;
    return true;
}

bool apply(double& d)
{
    std::cout << "double: " << d << std::endl;
    return true;
}

void test()
{
    typedef std::variant<int, double> asdf;

    std::vector<asdf> v;

    v.push_back(10);
    v.push_back(0.5);

    auto myLambda = [](auto&& arg) { return apply(arg); };  // use of auto (the 2nd one) is required.

    for (auto& elem : v)
    {
        std::visit(myLambda, elem);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.