C ++返回MyClass * vs MyClass

时间:2017-03-24 18:29:09

标签: c++ pointers object memory-leaks

我有一个我正在创建的模板类,我需要一个方法来返回该类的对象并分配它。我有一个方法,它接收2个对象,创建一个新对象,并返回它,以及赋值运算符的重载,以正确复制成员。

我试过这两种方法(两种方法都是静态的)。

方法1:有效,但这不是一种不好的做法吗?

template <class T>
MyClass<T>* MyClass<T>::MyFunction(const MyClass& a, const MyClass& b)
{
    MyClass<T> *newObject= new MyClass<T>(a.member, b.member2);

    //...do stuff to the object

    return newObject;
}

方法2:不起作用,但这不是一个更好的思路吗?

template <class T>
MyClass<T> MyClass<T>::MyFunction(const MyClass& a, const MyClass& b)
{
    MyClass<T> newObject(a.member, b.member2);

    //...do stuff to the object

    return newObject;
}

我对方法1的恐惧是我在堆中创建一个新对象而不是破坏它。我将其所有成员分配给另一个MyClass实例,但如果我第二次执行该操作,第一个实例会发生什么?我在堆栈上创建的第一个“新”对象是否只是坐在那里?

我对方法2的问题是在调用赋值操作重载之前,对象超出了范围。我查看了复制构造函数并移动构造函数。不幸的是,我没有看到在这种情况下被召唤的人(可能是这里缺乏经验)。

完成后,以下是相关代码的其余部分。

template <class T>
void MyClass<T>::operator = (const MyClass& b)
{
    if (this == &b) { return; }

    DeleteData();
    //just calls the same member removal operation as the destructor
    //to clear everything out to receive new data

    //...simple member copying stuff
}

template<class T>
inline void MyClass<T>::DeleteData()
{
    //call delete on members which need to be deleted manually
}

template <class T>
MyClass<T>::~MyClass()
{
    DeleteData();
}

template <class T>
MyClass<T>::MyClass(const T member, const T member2)
{
    //member initialization operations
}

示例用法:

MyClass<T> myObj1(member1, member 2);

MyClass<T> myObj2(member1, member 2);

MyClass<T> myObj3 = *(MyClass<T>::MyFunction(myObj1, myObj2));

TL; DR问题:

在函数内使用new返回class*而不是class时,创建的对象在不再有任何引用时会被销毁吗?

如果没有,返回在方法中创建的对象的最佳做法是什么?

如果是这样,这种方法是不好的做法吗?

我很抱歉,如果之前已经回答了这个问题,我对C ++的新感觉已经足够我以前的研究并没有给我一个满意的答案,可能是因为我不知道我不知道的事情。

实施解决方案: (我还添加了一个拷贝构造函数。没有它,这个解决方案仍然适用于问题的范围,但是有一些项目超出了这个问题的范围,由一个修复。希望未来有人可能会发现这个信息有用)

//this is a static function of MyClass
template <class T>
std::unique_ptr<MyClass<T>> MyClass<T>::MyFunction(const MyClass& a, const MyClass& b)
{
    std::unique_ptr<MyClass<T>> newObj = std::make_unique<MyClass<T>>(a.member, b.member2);

    //...Be the factory...

    return newObj ;
}

int main()
{
    MyClass<T> myObj1(member1, member 2);

    MyClass<T> myObj2(member1, member 2);

    MyClass<T> myObj3 = *(MyClass<T>::MyFunction(myObj1, myObj2));
}

3 个答案:

答案 0 :(得分:3)

  

在函数内使用new返回class*而不是class时,创建的对象在不再有任何引用时会被销毁吗?

不是。传递给new的任何内容都必须明确deleted,或传递给承担其所有权的容器(例如unique_ptr)。

  

如果是这样,这种方法是不好的做法吗?

这取决于,但通常是肯定的,不鼓励返回和处理原始指针,因为它容易发生内存泄漏。您上面的代码确实泄漏了(如您所述)。

  

如果没有,返回在方法中创建的对象的最佳做法是什么?

这取决于。如果您不介意复制,或者您的对象已定义移动语义,您可以返回方法2中的具体对象,并希望编译器执行return value optimization

如果您介意复制,请考虑某种returning a smart pointer

template <class T>
std::unique_ptr<MyClass<T>> MyClass<T>::MyFunction(const MyClass& a, const MyClass& b)
{
  auto newObject = std::unique_ptr<MyClass<T> {new MyClass<T>(a.member, b.member2)};
  //...do stuff to the object
  return newObject;
}

答案 1 :(得分:2)

这个怎么样:

template <class T>
std::shared_ptr<MyClass<T>> MyClass<T>::MyFunction(const MyClass& a, const MyClass& b)
{
    auto newObject = std::make_shared<MyClass<T>>(MyClass<T>(a.member, b.member2));

    //...do stuff to the object

    return newObject;
}

如果您考虑返回指针,这是在现代C ++中实现它的正确方法。共享指针将为您清除所有内容,并且您不会传递副本。假设您不想使用移动构造函数。

编辑:

由于人们在评论中批评了shared_ptr,因为它用火箭筒杀死了一只苍蝇(我同意),我也提供unique_ptr。请注意unique_ptr是一个必须移动而不是复制的唯一对象。

template <class T>
std::unique_ptr<MyClass<T>> MyClass<T>::MyFunction(const MyClass& a, const MyClass& b)
{
    auto newObject = std::unique_ptr<MyClass<T>>(new MyClass<T>(a.member, b.member2));

    //...do stuff to the object

    return newObject;
}

PS:请注意,我没有使用make_unique(而不是make_shared)。这是因为make_unique是C ++ 14,而不是C ++ 11。

答案 2 :(得分:0)

在C ++代码中,通常在任何地方传递指针是很常见的。一般来说,最好让指针的创建者也将其销毁。但是将指针的所有权传递给其他人也是可以接受的,让他们负责维护和销毁指针。

在你看来你的方法只不过是工厂方法的情况下尤其如此。将工厂方法视为与构造函数相同是完全可以的。而那个负责创造 - 并负责摧毁 - 指针的人。

如果您不太关心最佳做法,只是希望它对内存安全,那么您可以在std::shared_pointer中使用#include <memory>。这些行为有点像垃圾收集者。您可以像使用普通指针一样使用它们。但是一旦对它的最后一次引用超出范围,它就会为你删除底层指针。