提问者:小点点

为类成员使用智能指针


我很难理解智能指针在C++11中作为类成员的用法。我读过很多关于智能指针的文章,我想我确实了解一般是如何工作的。我不明白的是真正的用法。似乎每个人都建议使用作为几乎所有时间的方法。但我如何实现这样的东西:

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

让我们说我想用智能指针替换指针。因为,所以不能工作,对吗?这就是我使用的时间?无法使用?在我看来,在大多数情况下更有意义,除非我在很小的范围内使用指针?

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

这条路走吗?谢谢!


共2个答案

匿名用户

因为,所以不能工作,对吗?

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

它将单独成为对象的实例吗?当对象被销毁时,对象是否必须自动销毁,或者它应该比该对象活得更久?

在第一种情况下,是您所需要的,因为它使成为指向对象的唯一(唯一)所有者,也是唯一负责销毁对象的对象。

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

#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:您可能会想知道为什么我在这里使用原始指针,当每个人都在说原始指针是坏的,不安全的和危险的。实际上,这是一个非常宝贵的警告,但重要的是把它放在正确的上下文中:原始指针用于执行手动内存管理(即通过分配和释放对象)时是不好的。当纯粹用作实现引用语义和传递非拥有,观察指针的一种手段时,原始指针中没有任何本质上的危险,也许除了这样一个事实,即应该注意不要取消引用悬空指针。-末尾注1]

[注2:正如评论中所指出的,在所有权是唯一的并且所拥有的物体总是保证存在的这种特殊情况下(即:在所有人的财产中,所有人的财产都是唯一的),所有人的财产都是唯一的。内部数据成员永远不会是可以(也许应该)返回一个引用而不是指针。虽然这是真的,但我决定在这里返回一个原始指针,因为我的意思是这是一个简短的答案,可以概括为可以是的情况,并且表明只要不使用原始指针进行手动内存管理,它们就可以。-结束注释2]

当然,如果您的对象不应该拥有设备的独占所有权,那么情况就完全不同了。例如,如果对象的销毁不应意味着指向的对象也会被销毁,则可能会出现这种情况。

这是只有作为程序设计者的您才能知道的事情;从你提供的例子中,我很难分辨事实是否如此。

为了帮助您弄清问题,您可以问问自己,除了之外,是否还有任何其他对象有权使对象保持活动状态,只要它们持有指向该对象的指针,而不只是被动的观察者。如果确实如此,那么您需要共享所有权策略,这就是提供的:

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

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

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

匿名用户

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

仅用于引用循环。依赖关系图必须是循环有向图。在共享指针中,有2个引用计数:1用于中的指针时,应该使用来获取指针(如果存在的话)。