向量相关的内存分配问题

5
我遇到了以下的bug。
  • 我有一个类Foo。该类的实例被存储在class B的std::vectorvec中。
  • 在类Foo中,我使用new分配内存并通过~Foo()删除该对象,创建了类A的实例。

代码可以编译,但是运行时会崩溃。如果我从类Foo的析构函数中禁用delete my_a,那么代码可以正常运行(但是会存在内存泄漏)。

请问有人可以解释一下这里出了什么问题,并提供一个修复方法吗?

谢谢!

class A{
      public:
          A(int val);
          ~A(){};
          int val_a;

};

A::A(int val){
       val_a = val;
       };

class Foo {      
      public:
             Foo();
             ~Foo();
             void createA();
             A* my_a;
};

Foo::Foo(){
    createA();
};

void Foo::createA(){
    my_a = new A(20);
};

Foo::~Foo(){
    delete my_a;

};



class B {
      public:
             vector<Foo> vec;             
             void createFoo();            
             B(){};
             ~B(){};
};


void B::createFoo(){
    vec.push_back(Foo());
};


int main(){
    B b;

    int i  =0;
    for (i = 0; i < 5; i ++){
        std::cout<<"\n creating Foo";
        b.createFoo();   
        std::cout<<"\n Foo created";
        }
    std::cout<<"\nDone with Foo creation";

    std::cout << "\nPress RETURN to continue...";
    std::cin.get();

    return 0;
}
3个回答

7
你需要为Foo实现一个拷贝构造函数和一个赋值运算符。当你发现需要析构函数时,几乎肯定也需要这两个函数。它们在许多地方都有用,特别是将对象放入标准库容器中。
拷贝构造函数应该像这样:
Foo :: Foo( const Foo & f ) : my_a( new A( * f.my_a ) ) {
}

还有赋值运算符:

Foo & Foo :: operator=( const Foo & f ) {
    delete my_a;
    my_a = new A( * f.my_a );
    return * this;
}

或者更好的做法是,不要在Foo类中动态创建A实例:
class Foo {      
      public:
             Foo();
             ~Foo();
             void createA();
             A my_a;
};

Foo::Foo() : my_a( 20 ) {
};

1
Neil,你犯了个拼写错误... 将 new A( f.my_a ); 改为 new A( *f.my_a ); - TimW
嗨Neil,非常感谢你提供的代码片段。实际上,我想在将Foo实例放入向量时传递int valmy_a。我该怎么做呢? --> 我想要这样的东西:(当然它不起作用)vec.push_back(Foo():my_a(40) - memC
1
@memC 你需要给Foo构造函数传递一个整型参数,并将其传递给A构造函数。 - anon
@Neil:哦,我明白了。谢谢。Foo::Foo(int val):my_a(val)就可以了。谢谢。 - memC
@Neil,这段代码存在一些问题。1)new A(f.my_a) 应该在 两个 地方改为 new A(*f.my_a),2)赋值运算符没有处理 自我赋值 - Alex Budovski
显示剩余2条评论

3
如果您没有指定一个复制构造函数,编译器会为您生成一个。您的编译器生成的复制构造函数如下所示:
Foo::Foo(const Foo& copy)
    : my_a(copy.my_a)
{}

糟糕!你只复制了指针,而没有复制指向的内存。在createFoo()中,你的临时Foo()和复制到向量中的那个都指向同一块内存,因此这块内存会被删除两次,导致程序在第二次删除时崩溃。

你应该创建一个类似于以下代码的复制构造函数:

Foo::Foo(const Foo& copy)
    : my_a(new A(*copy.my_a))
{}

请注意,如果copy具有空的my_a成员,此代码将导致崩溃,并且它还会调用A的复制构造函数,而您还没有指定该函数。因此,您需要进行进一步更改。您还需要一个operator=重载。

2

Foo对象被复制,每个副本在销毁时都会调用相同指针值my_a上的delete方法。 实现Foo的复制和赋值运算符或使用智能指针。

 Foo( const Foo& s) : my_a( s.my_a ? new A(*s.my_a) : 0) {
 }

 Foo& operator= (const Foo& s) {
     Foo temp(s); 
     temp.swap (*this);
     return *this;
 }

 void swap (Foo &s) { 
     std::swap (my_a, s.my_a);  
 }; 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接