3

我正在编写一个智能指针 countedptr 并且遇到了减速带。countedptr 的基本功能是像任何其他智能指针一样工作,并且还计算有多少指针指向单个对象。到目前为止,代码是:

[解决了]

#include "std_lib_facilities.h"

template <class T>
class counted_ptr{
private:
    T* pointer;
    int* count;

public:
    counted_ptr(T* p = 0, int* c = new int(1)) : pointer(p), count(c) {}    // default constructor
    explicit counted_ptr(const counted_ptr& p) : pointer(p.pointer), count(p.count) { ++*count; } // copy constructor
    ~counted_ptr() { --*count; delete pointer; }

    counted_ptr& operator=(const counted_ptr& p)
    {
        pointer = p.pointer;
        count = p.count;
        ++*count;
        return *this;
    }
    T* operator->() const{ return pointer; }
    T& operator*() const { return *pointer; }

    int Get_count() const { return *count; }
};


int main()
{
    counted_ptr<double> one;
    counted_ptr<double>two(one);
    int a = one.Get_count();
    cout << a << endl;
}

当我尝试做类似的事情时

one->pointer = new double(5);

然后我收到一个编译器错误,提示“在 '*(&one)->counted_ptr::operator->with T = double' 中请求成员 'pointer',它是非类类型 double”。

我考虑过创建一个函数来执行此操作,虽然我可以创建一个函数来分配一个 T 数组,但我想不出一种方法来创建一个用于分配实际对象的方法。任何帮助表示赞赏,谢谢。

4

6 回答 6

5

旧解决方案

另一个赋值运算符呢?

counted_ptr& counted_ptr::operator=(T* p)
{
    if (! --*count) { delete count; }
    pointer = p;
    count = new int(1);
    return *this;
}

...

one = new double(5);

此外,您的析构函数总是删除一个共享指针,这可能是导致 *one 成为随机数的原因。也许你想要类似的东西:

counted_ptr::~counted_ptr() { if (! --*count) { delete pointer; delete count; } }

新解决方案

当您希望重新指向 counted_ptr (例如one = new double(5))以更新所有相关counted_ptr的 s 时,请将指针和计数都放在帮助程序类中,并让您的指针类保存指向帮助程序类的指针(您可能已经走上了这条路)。您可以通过两种方式填写此设计:

  1. 使辅助类成为一个简单的结构(和一个私有内部类),并将所有逻辑放在外部类方法中
  2. 制作counted_ptr助手类。counted_ptr维护一个引用计数,但不会自动更新计数;它不是智能指针,它只响应releaseretain消息。如果您完全熟悉 Objective-C,这基本上就是它的传统内存管理(除了自动释放)。counted_ptr当引用计数达到 0(与 Obj-C 的另一个潜在差异)时,可能会或可能不会删除自身。counted_ptrs 不应该是可复制的。目的是对于任何普通指针,最多应该有一个counted_ptr.

    创建一个smart_ptr具有指向 a 的指针的类,该指针在应该持有相同普通指针的实例counted_ptr之间共享。负责通过发送其释放和保留方法自动更新计数。smart_ptrsmart_ptrcounted_ptr

    counted_ptr可能是也可能不是shared_ptr.

这是选项二的界面。由于您将其作为练习进行,因此我将让您填写方法定义。潜在的实现将类似于已经发布的内容,只是您不需要复制构造函数和复制赋值运算符 for counted_ptrcounted_ptr::~counted_ptr不调用counted_ptr::release(这是smart_ptr::~smart_ptr的工作)并且counted_ptr::release可能无法释放counted_ptr::_pointer(您可能会将其留给析构函数) .

// counted_ptr owns its pointer an will free it when appropriate.
template <typename T>
class counted_ptr {
private:
    T *_pointer;
    size_t _count;

    // Make copying illegal
    explicit counted_ptr(const counted_ptr&);
    counted_ptr& operator=(const counted_ptr<T>& p);

public:
    counted_ptr(T* p=0, size_t c=1);
    ~counted_ptr();

    void retain();        // increase reference count.
    bool release();       // decrease reference count. Return true iff count is 0
    void reassign(T *p);  // point to something else.
    size_t count() const;

    counted_ptr& operator=(T* p);

    T& operator*() const;
    T* operator->() const;
};

template <typename T>
class smart_ptr {
private:
    counted_ptr<T> *_shared;
    void release();  // release the shared pointer
    void retain();   // retain the shared pointer

public:
    smart_ptr(T* p=0, int c=1);   // make a smart_ptr that points to p
    explicit smart_ptr(counted_ptr<T>& p); // make a smart_ptr that shares p
    explicit smart_ptr(smart_ptr& p); // copy constructor
    ~smart_ptr();

    // note: a smart_ptr's brethren are the smart_ptrs that share a counted_ptr.
    smart_ptr& operator=(smart_ptr& p); /* Join p's brethren. Doesn't alter pre-call
        * brethren. p is non-const because this->_shared can't be const. */
    smart_ptr& operator=(counted_ptr<T>& p);  /* Share p. Doesn't alter brethren. 
        * p is non-const because *this isn't const. */
    smart_ptr& operator=(T* p); // repoint this pointer. Alters brethren

    size_t count() const; // reference count

    T& operator*() const;  // delegate these to _shared
    T* operator->() const;

};

希望上面唯一模棱两可的点是故意的。

于 2009-08-22T01:48:41.417 回答
2

(对不起,这里是新手,不能发表评论)。Adatapost 添加的内容,“ one=new double(5);”应该可以工作。不过,还需要进行另一项更改:引用计数需要一点帮助。

...
~counted_ptr() {
    --*count; 
    // deallocate objects whose last reference is gone.
    if (!*count) 
    {   
        delete pointer;
        delete count;
    }
}

counted_ptr& operator=(const counted_ptr& p)
{
    // be careful to accommodate self assignment
    ++*p.count;

    // may lose a reference here
    --*count;
    if (!*count)
    {
        delete pointer;
        delete count;
    }

    count=p.count;
    pointer=p.pointer;
    return *this;
}

当然,这里有一些代码重复。将代码重构为自己的函数可能是有意义的,例如

private:
    /** remove our reference */
    void release()
    {
        --*count;
        if (!*count)
        {
            delete pointer;
            delete count;
        }
    }
于 2009-08-22T01:52:15.360 回答
1

你的意思是“ one.pointer=new double(5);”吗?写“ one->pointer=new double(5);”调用counted_ptr<double>::operator->。也就是说,它大致等价于:

double *tmp = one.operator->(); // returns one.pointer
tmp->pointer = new double(5);

但是双指针不是结构,因此它没有pointer成员。

于 2009-08-22T01:17:28.477 回答
1

由于眼前的问题已经解决,我想提供一些更长期的东西:

随着您继续开发此代码,您肯定希望将其提供给经验丰富的程序员进行全面审查,无论是在这里还是其他地方。在您发布代码时,您的代码存在一些明显的问题,尽管 outis 帮助纠正了这些问题。但是,即使您的代码全部编译并且似乎可以在您自己的测试中工作,也可能存在您尚未学会思考的测试和情况。智能指针很容易出现微妙的问题,直到非常具体的情况才会出现。因此,您会希望其他人查看您的代码以找到您可能遗漏的任何内容。

请不要将此视为对您当前代码的任何侮辱。我只是将其作为友好的建议提供,以确保您从该项目中学到最多。

于 2009-08-22T02:55:23.343 回答
0

除非您不是出于学术原因这样做,否则您可能需要考虑使用use_count(). boost::shared_ptr它并不完全有效,但它确实有效,你最好使用经过良好测试、成熟和线程安全的东西。如果您这样做是为了学习,请务必查看在更有效的 C++中对引用计数和智能指针的处理。

于 2009-08-22T01:57:47.677 回答
0

您需要减少计数,并可能在覆盖之前删除 operator = 中指向旧值的指针。您还需要在所有有“删除指针”的地方都使用“删除计数”以避免内存泄漏

于 2009-08-22T02:21:48.297 回答