2017-07-28 33 views
-2

说我有一个简单的类是这样的:待办事项构造对象分配内存泄漏


class MyObj 
{ 
char* myPtr; 
public: 
    MyObj() 
    { 
     myPtr = malloc(30); 
    } 
    ~MyObj() 
    { 
     free(myPtr); 
    } 
} 

class TestObject 
{ 
    MyObj _myObj; 

public: 
    TestObject(MyObj myObj) 
    { 
     _myObj = myObj; 
    } 
}; 

这是否泄漏内存?我的推理是,构造函数运行时TestObject已经包含了一个MyObj实例,所以在内存被释放之前,这并不能消除myPtr?分配给本地对象是否调用被替换的对象的析构函数?如果编译器直接在构造函数中赋值,编译器是否会优化对象实例变量的赋值?我来自C#,其中一个对象不会通过声明一个引用类型变量而自动初始化,所以这有点让我困惑。

谢谢!

+3

“这是否泄漏记忆?”你的C++教科书对此有何评论? –

+6

这两个泄漏内存*和*最终双释放内存。总之,你会被迫使这个*更多*错误比现在。 – WhozCraig

+0

为了不存在泄漏问题或双重问题,您需要遵循[rule-of-5](http://en.cppreference.com/w/cpp/language/rule_of_three)。或者走最佳路线并使用'std :: unique_ptr' – Justin

回答

3

这是否泄漏记忆?

myObj的分配将调用默认的复制分配操作符,因为您没有提供覆盖。因此,会执行逐个成员的副本,并且分配目标的myPtr实例将被来自分配源的myPtr实例覆盖。这里介绍两个问题,违反了Rule of Three/Five/Zero的一个或多个部件时,经常遇到:

  1. 您失去从分配的目标原来myPtr内容。因此,该指针唯一引用的原始内存被泄漏。
  2. 您现在的分享相同的指针值在两个myPtr成员:来源分配操作的目标。

后者特别令人不安,因为myObjTestObject构造函数中完成赋值后立即离开范围。在这样做的时候,myObj将被销毁,并且与此同时,它被释放了myPtr。此外,myObj通过到该构造函数,而不是引用,所以隐式副本已经可能发生(由于右值移动语义,缺乏副本)。因此, MyObj对象很可能被吊装myPtr所有引用相同的内存,并尽快发布一个它,其余的都在不知不觉中提升晃来晃去指针。任何解引用或这些指针的调用将调用未定义的行为

给本地对象调用分配对象的析构函数吗?

只有调用了析构函数才能与它们同名。也就是说,只有当一个对象被破坏被破坏时(手动调用析构函数进行放置 - 新的语义)时才会调用它们。除非引入临时对象,否则复制分配不会执行该操作,而代码中则不是这种情况。

如果编译器直接在构造函数中赋值,编译器是否会优化对象实例变量的赋值?

不,但member initialization list可以在这方面提供帮助。


现代C++编程技术经常使用RAII来完成你仿佛是试图以多种方式,这取决于你真正想要达到的目标。

唯一数据每个实例

如果目标是每个实例独有的动态数据,您可与std::vector<char>,或者干脆std::string容易做到这一点,取决于底层需求。两者都是RAII数据类型,通常对于动态内存管理需求而言是充足的。

class MyObj 
{ 
    std::vector<char> myData; 
public: 
    MyObj() : myData(30) 
    { 
    } 
} 

class TestObject 
{ 
    MyObj _myObj; 

public: 
    TestObject(MyObj myObj) 
     : _myObj(std::move(myObj)) 
    { 
    } 
}; 

这消除MyObj需要析构函数,并利用移动语义以及在TestObject构造上述成员初始化列表。 MyObj的所有实例都将提升一个不同的向量charMyObjTestObject的所有分配操作都使用默认实现。

分配共享内存

不太可能你想要这个,但它是没有最不具有可行性:

class MyObj 
{ 
    std::shared_ptr<char> myPtr; 
public: 
    MyObj() : myPtr(new char[30]) 
    { 
    } 
}; 

class TestObject 
{ 
    MyObj _myObj; 

public: 
    TestObject(MyObj myObj) 
     : _myObj(std::move(myObj)) 
    { 
    } 
}; 

类似的代码,但不同的成员类型。现在myPtrshared pointerchar的数组。任何分配给不同的myPtr都将加入共享列表。简而言之,分配意味着两个对象都参考相同的数据,并且引用计数可以确保最后一个人能够清除混乱。

注意:使用像这样的共享指针可能导致内存泄漏,因为new可能会成功,但共享指针的共享数据块可能会引发异常。这是在C++ 17, 其中std::make_shared支持数组分配


这些做什么,你可能会尝试完成的只是一些方法解决。我鼓励您在所提供的链接和本网站上阅读关于Rule of Three/Five/Zero和约RAII的概念。有很多例子可能会回答您可能遇到的更多问题。

+0

谢谢!超级有用。 –