【问题标题】:Circular references between two classes两个类之间的循环引用
【发布时间】:2012-06-20 19:48:55
【问题描述】:

我知道这一定是一个n00b问题,但我必须实现一个模型客户端-服务器顺序交互应用程序,并且由于客户端-服务器调用的数量不同,我不能只在外部函数中迭代步骤,总是获取来自客户端的数据,然后将其转发到服务器,反之亦然,所以我需要让我的 ServerClient 类相互了解,以便它们可以在它们之间调用它们的公共方法。一种方法是将两者都设计为单例,但我希望以更简单的方式来实现,更准确地说是使用循环引用:客户端存储对服务器的引用,服务器存储对客户端的引用。我知道这可能不是一个好方法,它可能会导致调用堆栈爆炸when it becomes too deep,因此欢迎对我的设计进行任何改进。

为了实现所描述的实现,我认为我可以使用std::shared_ptr,因为如果我还想防止 main 中的两个变量在调用两个 setter 时被破坏,std::unique_ptr 将不起作用(对?)。所以,这就是我所拥有的(简化代码):

#include <iostream>
#include <memory>

class Server;

class Client
{
public:
    void SetServer (const Server &server);
private:
    std::shared_ptr<const Server> server;
};

void Client::SetServer (const Server &server)
{
    this->server = std::shared_ptr<const Server>(&server);
}

class Server
{
public:
    void SetClient (const Client &client);
private:
    std::shared_ptr<const Client> client;
};

void Server::SetClient (const Client &client)
{
    this->client = std::shared_ptr<const Client>(&client);
}

int main ()
{
    Server server;
    Client client;

    server.SetClient(client);
    client.SetServer(server);

    //Here I ask the client to start interacting with the server.
    //The process will terminate once the client
    //exhausts all the data it needs to send to the server for processing

    return 0;
}

不幸的是,我的代码似乎试图多次调用客户端和服务器(隐式)析构函数,或者一些类似的讨厌的事情,我确信这是由于我对 std::shared_ptr 的理解不足造成的工作。请指教。

【问题讨论】:

  • +1 表示自包含的完整示例程序。 sscce.org.
  • 您将服务器和客户端参数作为引用传递给 SetXxxx。这不是人们通常会这样做的方式。我认为将这些作为指针传递是惯用的。理想情况下,作为智能指针,而不是裸指针。
  • @KubaOber 我会努力解决的。感谢您指出问题。

标签: c++ pointers client-server pass-by-reference circular-reference


【解决方案1】:

您在堆栈上分配服务器和客户端实例,当main() 退出时它们将被删除。你不希望std::shared_ptr 也删除它们。因此,有两种解决方案:

  1. 在客户端和服务器中使用非托管指针,并在外部管理它们的生命周期。

  2. 在任何地方都使用托管指针,包括main()。请注意,shared_ptr 意味着所有权。在当前设计中,服务器拥有客户端,但客户端也拥有服务器。这是一个循环引用:默认情况下,它们永远不会被释放,除非您在仍然可以的时候重置其中一个指针。

    例如,您可以决定客户端保持服务器处于活动状态,因此如果没有其他 shared_ptrs 指向它,最后消失的客户端将关闭服务器。服务器向客户端发送weak_ptr,但客户端向服务器发送shared_ptr

class Client;
class Server;

class Client
{
public:
    void SetServer (const std::shared_ptr<const Server> &server);
private:
    std::shared_ptr<const Server> server;
};

void Client::SetServer (const std::shared_ptr<const Server> &server)
{
    this->server = server;
}

class Server
{
public:
    void SetClient (const std::weak_ptr<const Client> &client);
private:
    std::weak_ptr<const Client> client;
};

void Server::SetClient (const std::weak_ptr<const Client> &client)
{
    this->client = client;
}


int main()
{
  std::shared_ptr<Server> server(new Server);
  std::shared_ptr<Client> client(new Client);

  server->SetClient(client);
  client->SetServer(server);

  // do stuff

  return 0;
}

【讨论】:

  • +1。重要的是要记住,您放弃了删除用于构造 std::shared_ptr 的对象所指向的对象的责任。
  • 您不能从原始指针构造weak_ptr。您只能从shared_ptr 或另一个weak_ptr 构造weak_ptr
  • @KubaOber 是的,这真的很好,而且似乎有效。谢谢!不幸的是,我觉得只插入我不能正确理解的代码是愚蠢的。您能否详细说明一下细节,例如窗帘后面发生了什么以及为什么使用std::weak_ptr?什么决定了释放指针和调用析构函数的顺序?如果我们假设服务器是并且永远只是一个,我的常识告诉我应该使用std::unique_ptr
  • 另外,还有一个愚蠢的问题:为什么不在Client 类中使用std::weak_ptr 作为内部服务器引用呢?我阅读了这篇文章msdn.microsoft.com/en-us/library/bb982126.aspx,但这个问题的答案仍然让我难以捉摸。
  • 没关系。我在这里找到了一个很好的解释stackoverflow.com/a/7473652/1174378 :)
【解决方案2】:

您的变量具有自动生命周期,并且在超出范围时将被销毁。

因此,使用任何生命周期管理智能指针都是错误的,因为它会再次调用 delete。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-09
    • 1970-01-01
    相关资源
    最近更新 更多