2017-12-18 143 views
0

自大学以来没有使用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 

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

将构造函数/析构函数移到公共虚拟父类上不应该(事实上也没有)改变它。

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

主:

#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 
+1

你'buildBar()'函数是内存泄漏的向量提取的元素。 – juanchopanza

+3

“_Initially我使用了一个向量指针,它的工作,但如果我理解正确,这将在清除时泄漏内存。”所以,为什么不使用'std :: unique_ptr'的std :: vector?相关阅读:[什么是对象切片?](https://stackoverflow.com/questions/274626/what-is-object-slicing) –

+2

'sizeof(Foos)'不会告诉你向量中有多少项。 (它会告诉你它占用了多少字节,但不包含它所包含的数据)。 – Galik

回答

1

首先,良好的对你进行讯问的原始指针的使用!人们往往盲目地使用它们,并最终导致其他问题。

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

根据您的使用,您可以使用std::unique_ptrstd::shared_ptr,或std::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); 
    } 
} 
+0

谢谢。我现在正在阅读这个变体。它是否允许我直接访问子类的公共函数,而不必在父类中声明它们是虚拟的还是重铸?由于这是我自己使用的,所以对应用于错误类的操作抛出异常是可以接受的,甚至是可取的。 – MGreene

+0

是的。由于您没有使用指针,因此编译器可以在编译时确定函数链接,并且根本不使用动态绑定。事实上,这些类型不必是相关的。 –

+0

我添加了一个如何使用变体向量中的元素的例子。 –