C++ 共享的真实生活用例

C++ 共享的真实生活用例,c++,c++11,shared-ptr,C++,C++11,Shared Ptr,当我们需要一个动态分配项目的多个所有者时,将使用共享的ptr 问题是,我无法想象我们需要多个所有者的场景。我能想象的每一个用例都可以用一个独特的ptr来解决 有人能提供一个真实的用例示例,其中包含需要共享的ptr的代码(我所说的“需要”,是指作为智能指针的最佳选择)?我所说的“真实生活”是指一些实用的用例,而不是一些过于抽象和虚构的东西 取在类C的成员函数f中调用的任何lambda,其中要处理的对象将作为引用传递到lambda[&]中。当您在f内等待lambda完成时,C超出了范围。函数消失了,

当我们需要一个动态分配项目的多个所有者时,将使用共享的ptr

问题是,我无法想象我们需要多个所有者的场景。我能想象的每一个用例都可以用一个独特的ptr来解决


有人能提供一个真实的用例示例,其中包含需要共享的ptr的代码(我所说的“需要”,是指作为智能指针的最佳选择)?我所说的“真实生活”是指一些实用的用例,而不是一些过于抽象和虚构的东西

取在类C的成员函数f中调用的任何lambda,其中要处理的对象将作为引用传递到lambda[&]中。当您在f内等待lambda完成时,C超出了范围。函数消失了,您有一个悬空引用。当lambda下一次访问引用时,分段错误与定义的行为最接近。您不能将唯一的投注者传递到lambda。一旦它被移动,您就无法从f访问它。解决方案:共享指针和[=]。我对数据库的核心进行编码。在多线程基础设施中,我们需要始终共享指针。别忘了原子参考计数器。但你的普遍怀疑是值得赞赏的。在一个CAD应用程序中,当多个模型恰好具有相同的网格时(例如,在用户复制粘贴这些模型后),我使用Shared_ptr来保存RAM和VRAM。另外,多个线程可以同时访问网格,因为正确使用共享_ptr和弱_ptr都是线程安全的

下面是一个简单的例子。由于许多原因(GPU缓冲区、鼠标拾取、由某些用户输入触发的后台处理等),真正的代码要复杂得多,但我希望这足以让您了解共享ptr的合理性

// Can be hundreds of megabytes in these vectors
class Mesh
{
    std::string name;
    std::vector<Vector3> vertices;
    std::vector<std::array<uint32_t, 3>> indices;
    BoundingBox bbox;
};

// Just 72 or 80 bytes, very cheap to copy.
// Can e.g. pass copies to another thread for background processing.
// A scene owns a collection of these things.
class Model
{
    std::shared_ptr<Mesh> mesh;
    Matrix transform;
};
//在这些向量中可以有数百兆字节
类网格
{
std::字符串名;
向量顶点;
std::向量指数;
边界框bbox;
};
//只有72或80字节,拷贝起来非常便宜。
//例如,可以将副本传递给另一个线程进行后台处理。
//场景拥有这些东西的集合。
类模型
{
std::共享的ptr网格;
矩阵变换;
};

在我的程序的用户界面中,我有“控制点值”的概念(一个
控制点值
表示我的程序控制的硬件上控件的当前状态),当然还有“小部件”的概念(小部件是一个GUI组件,它将控制点的当前状态呈现给监视器,供用户查看和/或操作)

由于这是一个需要控制的相当复杂的系统,我们有

  • 大量不同类型的
    控制点值
    (浮点、整数、字符串、布尔、二进制blob等)
  • 大量不同类型的
    小部件
    (文本显示、音量控制器、仪表、旋钮、按钮等)
  • 给定的
    小部件
    可以选择多种不同的方式将特定的
    控制点值
    呈现为文本(大写、小写、精度的多位或少位等)
如果我们只是做了一件显而易见的事情,每次我们需要上面的新组合时都写一个新的子类,那么我们最终会得到成千上万个子类的几何爆炸,因此会有一个非常大的代码库,很难理解或维护

为了避免这种情况,我将“如何以某种特定方式将
控制点值
转换为人类可读的文本”的知识分离为它自己的不可变对象,任何人都可以使用它来进行转换,例如

// My abstract interface
class IControlPointToTextObject
{
public:
   virtual std::string getTextForControlPoint(const ControlPoint & cp) const = 0;
};

// An example implementation
class RenderFloatingPointValueAsPercentage : public IControlPointToTextObject
{
public:
   RenderFloatingPointValueAsPercentage(int precision) : m_precision(precision)
   {
      // empty
   }

   virtual std::string getTextForControlPoint(const ControlPoint & cp) const = 0
   {
      // code to create and return a percentage with (m_precision) digits after the decimal point goes here....
   }

private:
   const int m_precision;
};
…到目前为止,还不错;例如,当我希望文本小部件以百分比形式显示控制点值,小数点后有3位数字时,我可以这样做:

TextWidget * myTextWidget = new TextWidget;
myTextWidget->setTextRenderer(std::unique_ptr<IControlPointToTextObject>(new RenderFloatingPointValueAsPercentage(3)));

不必担心对象生命周期,不必担心悬空指针,不必担心内存泄漏,也不必创建重复的渲染器对象。C'est bon:)

假设我要为一种语言实现一个递归的“表达式”定义。解析不仅要检查输入是否符合语法,还要输出一些可以用来进行分析、计算、编译等的东西。我需要一些东西来表示每个表达式或子表达式语法符号的结果。每个语法规则的实际语义可以用多态性表示,因此这需要某种指向基类
表达式的指针

然后,自然表示为
std::shared_ptr
。一个
表达式
对象可以是另一个复合
表达式
的子表达式,在这种情况下,复合
表达式
是子表达式的所有者。或者,对于尚未与其他片段组合的语法产品,
表达式
对象可以由正在进行的算法的解析堆栈拥有。但并非两者同时存在。如果我正在编写一个LALR解析器,我可能需要
std::unique_ptr
,当每个语法符号减少时,将子表达式从解析堆栈传输到复合表达式构造函数

GLR算法对共享ptr的特殊需求。在某些情况下,当到目前为止扫描的输入有多个可能的解析时,算法将复制解析堆栈,以便尝试对每个可能的解析进行尝试。随着试探性分析的进行,每个可能性可能需要使用它自己的分析堆栈中的一些中间结果来形成一些复合表达式的子表达式,所以现在我们可能有相同的
表达式
被一些解析堆栈和一些不同的复合
表达式
对象使用
std::shared_ptr<IControlPointToTextObject> threeDigitRenderer = std::make_shared<RenderFloatingPointValueAsPercentage>(3);

myWidget1->setTextRenderer(threeDigitRenderer);
myWidget2->setTextRenderer(threeDigitRenderer);
myWidget3->setTextRenderer(threeDigitRenderer);
[...]
class Endpoint {
public:
    // Fill in sender address, etc., in msg, then send it to all
    // subscribers on topic.
    void send(std::unique_ptr<Message> msg, TopicId topic);

    // Register this endpoint as a subscriber to topic, with handler
    // called on receiving messages on that topic.
    void subscribe(TopicId topic,
        std::function<void(std::shared_ptr<const Message>)> handler);
};
class frame { };

class consumer { public: virtual void draw(std::shared_ptr<frame>) = 0; };

class screen_consumer_t :public consumer { public:  void draw(std::shared_ptr<frame>) override {} };
class matrox_consumer_t :public consumer { public:  void draw(std::shared_ptr<frame>) override {} };
class decklink_consumer_t :public consumer { public:  void draw(std::shared_ptr<frame>) override {} };

int main() {
    std::shared_ptr<frame> current_frame = std::make_shared<frame>();

    std::shared_ptr<consumer> screen_consumer = std::make_shared<screen_consumer_t>();
    std::shared_ptr<consumer> matrox_consumer = std::make_shared<matrox_consumer_t>();
    std::shared_ptr<consumer> decklink_consumer = std::make_shared<decklink_consumer_t>();

    std::vector<consumer> consumers;
    consumers.push_back(screen_consumer);
    consumers.push_back(matrox_consumer);
    consumers.push_back(decklink_consumer);

    //screen_consumer->draw(current_frame);
    //matrox_consumer->draw(current_frame);
    //decklink_consumer->draw(current_frame);

    for(auto c: consumers) c->draw(current_frame);


}
struct node_t
{
    std::unique_ptr<board_t> board_;
    std::weak_ptr<node_t> parent_;
    std::vector<std::shared_ptr<node_t>> children_;
};