实现智能指针 - 使用模板进行动态分配

发布于 2024-08-03 10:27:32 字数 1279 浏览 5 评论 0原文

我正在编写一个智能指针 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',属于非类类型 double”。

我考虑创建一个函数来执行此操作,虽然我可以创建一个函数来分配 T 数组,但我想不出一种方法来创建一个函数来分配实际对象。如有任何帮助,我们将不胜感激,谢谢。

I'm in the process of writing a smart pointer countedptr and I've hit a speed bump. The basic function of countedptr is to work like any other smart pointer and also have a count of how many pointers are pointing to a single object. So far, the code is:

[SOLVED]

#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;
}

When I try to do something like

one->pointer = new double(5);

then I get a compiler error saying "request for member 'pointer' in '*(&one)->counted_ptr::operator->with T = double' which is of non-class type double".

I considered making a function to do this, and while I could make a function to allocate an array of T's, I can't think of a way of making one for allocating actual objects. Any help is appreciated, thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

硬不硬你别怂 2024-08-10 10:27:32

旧的解决方案

另一个赋值运算符怎么样?

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 时,请将指针和帮助程序类中的计数,并让您的指针类保存指向该帮助程序类的指针(您可能已经沿着这条路径走下去)。您可以采用两种方法来完成此设计:

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

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

    counted_ptr 可能是也可能不是 shared_ptr 的私有内部类。

这是选项二的界面。由于您将此作为练习,因此我将让您填写方法定义。潜在的实现与已经发布的类似,只是您不需要 counted_ptr 的复制构造函数和复制赋值运算符,counted_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;

};

希望以上唯一含糊之处是有意为之的。

Old Solution

What about another assignment operator?

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

...

one = new double(5);

Also, your destructor always deletes a shared pointer, which is probably what caused *one to be a random nomber. Perhaps you want something like:

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

New Solution

As you want repointing a counted_ptr (eg one = new double(5)) to update all related counted_ptrs, place both the pointer and the count in a helper class, and have your pointer class hold a pointer to the helper class (you might already be headed down this path). You could go two ways in filling out this design:

  1. Make the helper class a simple struct (and a private inner class) and place all the logic in the outer class methods
  2. Make counted_ptr the helper class. counted_ptr maintains a reference count but doesn't automatically update the count; it's not a smart pointer, it only responds to release and retain messages. If you're at all familiar with Objective-C, this is basically its traditional memory management (autoreleasing aside). counted_ptr may or may not delete itself when the reference count reaches 0 (another potential difference from Obj-C). counted_ptrs shouldn't be copyable. The intent is that for any plain pointer, there should be at most one counted_ptr.

    Create a smart_ptr class that has a pointer to a counted_ptr, which is shared among smart_ptr instances that are supposed to hold the same plain pointer. smart_ptr is responsible for automatically updating the count by sending its counted_ptr release and retain methods.

    counted_ptr may or may not be a private inner class of shared_ptr.

Here's an interface for option two. Since you're doing this as an exercise, I'll let you fill out the method definitions. Potential implementations would be similar to what's already been posted except that you don't need a copy constructor and copy assignment operator for counted_ptr, counted_ptr::~counted_ptr doesn't call counted_ptr::release (that's smart_ptr::~smart_ptr's job) and counted_ptr::release might not free counted_ptr::_pointer (you might leave that up to the destructor).

// 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;

};

Hopefully, the only ambiguous points above are the intentional ones.

︶葆Ⅱㄣ 2024-08-10 10:27:32

(抱歉,这里是新手,无法发表评论)。 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;
        }
    }

(Sorry, newbie here, and can't leave comments). What Adatapost added, "one=new double(5);" should work. One other change needed, though: the reference counting needs a little help.

...
~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;
}

Of course, there's some code repetition here. It might make sense to refactor that code into its own function, e.g.

private:
    /** remove our reference */
    void release()
    {
        --*count;
        if (!*count)
        {
            delete pointer;
            delete count;
        }
    }
沩ん囻菔务 2024-08-10 10:27:32

也许您的意思是“one.pointer=new double(5);”?写入“one->pointer=new double(5);”会调用 counted_ptr::operator->。也就是说,它大约相当于:

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

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

Did you, perhaps, mean "one.pointer=new double(5);"? Writing "one->pointer=new double(5);" invokes counted_ptr<double>::operator->. That is, it is approximately equivalent to:

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

But a double pointer isn't a structure, and so it doesn't have a pointer member.

烦人精 2024-08-10 10:27:32

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

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

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

Since the immediate problem has already been solved, I want to offer something more long term:

As you continue to develop this code, you'll definitely want to offer it up for full review by experienced programmers, whether here or elsewhere. There were a few obvious problems with your code as you posted it, though outis has helped correct them. But even once your code all compiles and seems to work in your own tests, there may be tests and situations which you haven't yet learned to think about. Smart pointers can easily have subtle problems that don't show up until very specific situations. So you'll want others to look over your code to find anything which you may have missed.

Please don't take this as any kind of insult towards your current code. I'm just offering this as friendly advice to ensure you learn the most you can out of this project.

っ〆星空下的拥抱 2024-08-10 10:27:32

除非您不是出于学术原因这样做,否则您可能需要考虑使用 use_count() boost::shared_ptr 的成员。它并不完全有效,但它确实有效,并且您最好使用经过充分测试、成熟且线程安全的东西。如果您这样做是出于学习目的,请务必查看 更有效的 C++

Unless you are not doing this for academic reasons, you might want to use consider using the use_count() member of boost::shared_ptr. It's not entirely efficient, but it does work and you're better off using something well tested, mature, and thread safe. If you are doing this for learning purposes, be sure to check out the treatment of Reference Counting and Smart Pointers in More Effective C++.

遥远的绿洲 2024-08-10 10:27:32

在覆盖之前,您需要递减计数并可能删除指向运算符 = 中旧值的指针。在有“删除指针”的地方还需要“删除计数”以避免内存泄漏

You need to decrement the count and possibly delete the pointer to the old value in operator = before you overwrite it. You also need 'delete count' everywhere you have 'delete pointer' to avoid leaking memory

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文