无法按值复制的指针容器类

时间:2009-01-10 06:31:01

标签: c++ templates

我的项目需要一个智能指针,可以作为参数发送给多个方法。我已经检查了来自boost的 auto_ptr shared_ptr 。但IMO,这不符合我的要求。以下是我的发现

auto_ptr:当传递给另一个方法时,将转移所有权,并在该方法的范围结束时删除基础指针。我们可以通过引用传递auto_ptr来解决这个问题,但是没有编译时机制来确保它总是通过引用传递。如果错误地,用户忘记传递引用,则会产生问题。

boost :: shared_ptr:这看起来很有前途,可以满足我的需要。但我觉得这对我的项目来说太过分了,因为它很小。

所以我决定编写一个简单的模板化指针容器类,它不能被值复制并注意删除底层指针。这是

template <typename T>
class simple_ptr{
public:
    simple_ptr(T* t){
       pointer = t;
    }
    ~simple_ptr(){
       delete pointer;
    }
    T* operator->(){
       return pointer;
    }
private: 
    T* pointer;
    simple_ptr(const simple_ptr<T>& t);
};

此实施是否正确?我已经将复制构造函数设置为私有,因此当有人试图按值传递时,编译器会发出警报。

如果删除了指针,删除操作就会抛出断言错误。我该如何解决这个问题?

我对C ++很陌生,非常感谢你的建议。

由于

7 个答案:

答案 0 :(得分:7)

请按照Martin York的建议使用boost::scoped_ptr<>,因为它:

  • 完全符合您的要求(这是一个不可复制的指针)
  • 没有高于标准C指针的开销
  • 由超级智能C ++向导精心制作,以确保其表现符合预期。

虽然我看不到你的实现有任何问题(在应用了ChrisW建议的更改之后),C ++有很多黑暗的角落,如果有一些模糊的角落情况我不会感到惊讶你,我和其他人在这里没有发现。

答案 1 :(得分:3)

你做的是boost :: scoped_ptr

请同时阅读j_random_hacker的评论。

答案 2 :(得分:2)

  

此实施是否正确?我已将复制构造函数设为私有...

您可以为赋值运算符执行相同的操作:

simple_ptr& operator=(const simple_ptr<T>& t);

取消引用运算符的const版本也可能有用,并且智能指针通常也定义另一种解引用运算符:

const T* operator->() const { return pointer; }
const T& operator*() const { return *pointer; }
T& operator*() { return *pointer; }
  

如果删除了指针,删除操作就会抛出断言错误。我该如何解决这个问题?

你的意思是,如果我这样做:

//create instance
Car* car = new Car;
//assign to smart ptr
simple_ptr<Car> ptr(car);
//explicit delete
delete car;
//... assertion when ptr is destroyed ...

一种方法(我不知道它是否是方式)来防止这种情况是将构造函数和/或析构函数和/或T类的delete运算符声明为private,并说simple_ptr是T类的friend(因此只有simple_ptr类可以创建和/或销毁和/或删除T的实例)

  

在T类中将新运算符标记为私有似乎是不可能的,因为我必须修改将与simple_ptr一起使用的所有类

是的,这是真的:要在上面做我的建议,你需要修改类定义。

如果您的问题是“如何在不修改类定义的情况下进行双重删除?”那么我认为答案是:

  • 你不能:这是应用程序代码(使用这些类)要小心
  • 您可以:通过提供您自己的堆管理器,即您自己的全局运算符new和全局运算符delete的实现,并在您的smart_ptr代码中询问您的堆管理器,看看是否仍然分配了此指针的这种化身,删除之前

答案 3 :(得分:1)

要回答你的第一个问题,你可以得到的最好的保证就是在它周围实施测试工具来做一些简单的任务,并确保你得到你期望的行为。对我来说,代码是正确的,比一些随机的人阅读它的意见要好得多。

至于你的第二个问题,你可以通过在第一次删除它之后将指针设置为某个标记值来解决删除引发断言错误。类似的东西:

if (pointer) {
    delete pointer;
    pointer = NULL;
} else {
    error("Attempted to free already freed pointer.");
}

您将遇到的问题是您的重载 - &gt;运算符返回指针的值,这意味着无论你将其返回给谁也可以调用删除,导致我上面提出的检查不起作用。例如:

simple_ptr<int> myPtr = someIntPointer;
...
delete myPtr.operator->(); /* poof goes your pointered memory region */

我可能会建议你不要依赖 operator-&gt; 重载,并且只要求那些使用此类的人在将该值传递回用户之前调用一个在内部取消引用指针的方法。

希望这有帮助。

答案 4 :(得分:1)

你有两个选择:

  1. boost::scoped_ptr已经由j_random_hacker详细说明,因为它是不可复制的(不共享所有权,如shared_ptr)和不可移动(不像auto_ptr那样转移所有权.auto_ptr有一个复制构造函数,但是那个它没有复制。它将原始指针移动到* this)。 boost::scoped_ptr正是您所需要的。
  2. const auto_ptr不允许转让所有权。并通过引用const(auto_ptr<T> const&)来获取参数。如果函数的writer通过值接受,如果你尝试传递一个const auto_ptr它仍然无法工作,因为它的复制构造函数需要一个非const auto_ptr。
  3. 直到C ++ 1x,boost :: scoped_ptr是满足您需求的最佳选择,如果必须使用官方标准内容,则为const auto_ptr(阅读this)。在C ++ 1x中,您可以使用std::unique_ptr作为auto_ptr的更好替代方法,因为您必须明确说明何时要转移所有权。尝试复制它将导致编译时错误。 unique_ptr在this答案中有详细说明。

答案 5 :(得分:1)

你应该使用boost :: scoped_ptr&lt;&gt;正如已经提到的那样。

一般来说,如果你需要使一个类不可复制,你应该继承boost :: noncopyable,即

#include <boost/utility.hpp>

class myclass : boost::noncopyable
{
    ...
};

这使得所有工作都成为不可复制的,并且很好地自我记录。

答案 6 :(得分:0)

我见过有时使用简单的DISABLE_COPY宏:

#define DISABLE_COPY(Class) \
        Class(const Class &); \
        Class &operator=(const Class &);

因此,通常的做法是将复制构造函数和赋值运算符定义为您的任务的私有。