173

我无法理解智能指针在 C++11 中作为类成员的用法。我已经阅读了很多有关智能指针的内容,并且我认为我确实了解总体上是如何unique_ptrshared_ptr/weak_ptr工作的。我不明白的是真正的用法。似乎每个人都建议使用unique_ptr几乎所有时间。但是我将如何实现这样的事情:

class Device {
};

class Settings {
    Device *device;
public:
    Settings(Device *device) {
        this->device = device;
    }

    Device *getDevice() {
        return device;
    }
};    

int main() {
    Device *device = new Device();
    Settings settings(device);
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

假设我想用智能指针替换指针。Aunique_ptr不会工作,因为getDevice(),对吧?那是我使用shared_ptrand的时候weak_ptr吗?没办法使用unique_ptrshared_ptr在我看来,除非我在非常小的范围内使用指针,否则大多数情况下更有意义?

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> device) {
        this->device = device;
    }

    std::weak_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device(new Device());
    Settings settings(device);
    // ...
    std::weak_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

这是要走的路吗?非常感谢!

4

2 回答 2

218

Aunique_ptr不会工作,因为getDevice(),对吧?

不,不一定。这里重要的是为您的对象确定适当的所有权策略Device,即谁将成为您的(智能)指针所指向的对象的所有者。

它会单独Settings成为对象的实例吗?当对象被销毁时,该对象是否必须自动销毁,还是应该比该对象更长寿?DeviceSettings

在第一种情况下,std::unique_ptr这就是您所需要的,因为它是Settings指向对象的唯一(唯一)所有者,并且是唯一负责其销毁的对象。

在这种假设下,getDevice()应该返回一个简单的观察指针(观察指针是不保持指向对象存活的指针)。最简单的观察指针是原始指针:

#include <memory>

class Device {
};

class Settings {
    std::unique_ptr<Device> device;
public:
    Settings(std::unique_ptr<Device> d) {
        device = std::move(d);
    }

    Device* getDevice() {
        return device.get();
    }
};

int main() {
    std::unique_ptr<Device> device(new Device());
    Settings settings(std::move(device));
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

[注意 1: 您可能想知道为什么我在这里使用原始指针,因为每个人都一直在说原始指针是坏的、不安全的和危险的。实际上,这是一个宝贵的警告,但将其放在正确的上下文中很重要:原始指针在用于执行手动内存管理时是不好的,即通过newand分配和释放对象delete。当纯粹用作实现引用语义和传递非拥有的观察指针的一种手段时,原始指针本质上没有任何危险,除了可能要注意不要取消对悬空指针的引用这一事实。-结束注 1 ]

[注2: 正如评论中出现的那样,在这种特殊情况下,所有权是唯一的,并且始终保证拥有的对象存在(即内部数据成员device永远不会存在nullptr),函数getDevice()可以(并且可能应该)返回引用而不是指针。虽然这是真的,但我决定在这里返回一个原始指针,因为我的意思是这是一个简短的答案,可以概括为 where devicecould be的情况nullptr,并表明只要不将原始指针用于手动内存管理。-结束注 2 ]


当然,如果您的Settings对象不应该拥有设备的独占所有权,情况就完全不同了。例如,如果Settings对象的破坏不应该暗示指向的Device对象的破坏,则可能是这种情况。

这是只有作为程序设计者的你才能说出的;从您提供的示例中,我很难判断是否是这种情况。

为了帮助您弄清楚,您可能会问自己是否还有其他对象Settings有权保持该Device对象的活动,只要它们持有指向它的指针,而不仅仅是被动的观察者。如果确实如此,那么您需要一个共享所有权策略,它std::shared_ptr提供:

#include <memory>

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> const& d) {
        device = d;
    }

    std::shared_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device = std::make_shared<Device>();
    Settings settings(device);
    // ...
    std::shared_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

请注意,这weak_ptr是一个观察指针,而不是拥有指针 - 换句话说,如果指向指向对象的所有其他拥有指针超出范围,它不会使指向对象保持活动状态。

与常规原始指针相比的优势weak_ptr在于,您可以安全地判断是否weak_ptr悬空即它是否指向有效对象,或者最初指向的对象是否已被销毁)。这可以通过调用对象的expired()成员函数来完成weak_ptr

于 2013-03-26T23:06:56.337 回答
1
class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(const std::shared_ptr<Device>& device) : device(device) {

    }

    const std::shared_ptr<Device>& getDevice() {
        return device;
    }
};

int main()
{
    std::shared_ptr<Device> device(new Device());
    Settings settings(device);
    // ...
    std::shared_ptr<Device> myDevice(settings.getDevice());
    // do something with myDevice...
    return 0;
}

week_ptr仅用于参考循环。依赖图必须是无环有向图。在共享指针中有 2 个引用计数:1 个用于shared_ptrs,1 个用于所有指针(shared_ptrweak_ptr)。当所有shared_ptr的 s 都被删除时,指针被删除。当需要来自 的指针时weak_ptrlock应该使用它来获取指针(如果存在)。

于 2013-03-26T22:53:44.913 回答