【问题标题】:Passing a reference to an uninitialised object to a super class constructor and then initialising said object with it's move constructor afterwards?将对未初始化对象的引用传递给超类构造函数,然后使用它的移动构造函数初始化所述对象?
【发布时间】:2015-12-28 19:19:20
【问题描述】:

我正在编写一个 C++ websocket 服务器库。在我提供的一个示例中,我使用了两个类session_basesession。我这样做是为了让session_base 父类中的tcp::socket 对象初始化(使用移动构造函数),然后再将它的引用传递给ws::session<tcp::socket> 父类,该父类存储此引用以供以后使用。我创建 ws:session 作为模板类的原因是我可以使用boost::asio::ssl::streams 以及 tcp 套接字。

tcp::socket 对象成为session 类的成员是否有效,将此未初始化对象的引用传递给ws::session 构造函数(它尚未使用tcp::socket - 仅存储引用),然后使用套接字移动构造函数初始化tcp::socket 对象?

当前代码:

using boost::asio::ip::tcp;

class session_base {
public:
    session_base(tcp::socket socket) : socket_(std::move(socket)) { }
protected:
    tcp::socket socket_;
};

using T = tcp::socket;
class session : public session_base, public ws::session<T> {
public:
    session(tcp::socket socket) :
        session_base(std::move(socket)), ws::session<T>(socket_)
    {
        std::cout << "session()\n";
    }

    ~session() {
        std::cout << "~session()\n";
    }

private:
    void on_open() override {
        std::cout << "WebSocket connection open\n";
    }

    void on_msg(const ws::message &msg) override {
        /* Do stuff with msg */

        read();
    }

    void on_close() override {
        std::cout << "WebSocket connection closed\n";
    }

    void on_error() override {
        std::cout << "WebSocket connection error\n";
    }
};

建议代码:

下面建议的代码对我有用,但我想知道这是已定义的行为并推荐。

using boost::asio::ip::tcp;

using T = tcp::socket;
class session : public ws::session<T> {
public:
    session(tcp::socket socket) :
        ws::session<T>(socket_), socket_(std::move(socket))
    {
        std::cout << "session()\n";
    }

    ~session() {
        std::cout << "~session()\n";
    }

private:
    tcp::socket socket_;

    void on_open() override {
        std::cout << "WebSocket connection open\n";
    }

    void on_msg(const ws::message &msg) override {
        /* Do stuff with msg */

        read();
    }

    void on_close() override {
        std::cout << "WebSocket connection closed\n";
    }

    void on_error() override {
        std::cout << "WebSocket connection error\n";
    }
};

完整来源:https://github.com/alexyoung91/ws

【问题讨论】:

标签: c++ oop boost


【解决方案1】:

如果ws::session 构造函数只存储对套接字的引用,但不使用引用来调用套接字成员函数或访问数据成员,则程序是良构的。标准将此称为对 3.8/6 ([basic.life/6]) 中分配存储的引用:

类似地,在对象的生命周期开始之前但在对象将占用的存储空间分配之后,或者在对象的生命周期结束之后并且在对象的存储空间之前 占用被重用或释放,任何引用原始对象的glvalue都可以使用,但只能以有限的方式使用。对于正在建造或破坏的物体,见 12.7。否则,这样的glvalue指的是分配的存储(3.7.4.2),并且使用不依赖于其值的glvalue的属性是明确定义的。如果出现以下情况,则程序具有未定义的行为:

——左值到右值的转换 (4.1) 应用于这样的左值,

——glvalue 用于访问非静态数据成员或调用对象的非静态成员函数,或

——glvalue 被隐式转换 (4.10) 为对基类类型的引用,或者

— 泛左值用作 static_cast (5.2.9) 的操作数,除非最终转换为 cv char&amp;cv @987654325 @,或

— 泛左值用作dynamic_cast (5.2.7) 的操作数或typeid 的操作数。

注意,必须在获取引用时分配存储,调用session构造函数时也是如此。

但尽管如此,我不推荐这种方法。主要是因为在ws::session 构造函数中很容易忘记传递的引用是指尚未初始化的对象,并在以后引入错误。最好使用base-from-member idiom 并保留您的原始代码。

【讨论】:

  • 我尝试使用 boost::base_from_member,它的好处是不必编写 session_base 类,但我认为它不支持 tcp::socket 所需的移动构造函数。因此在阅读您的答案后决定保留我的原始代码,谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-09-13
  • 1970-01-01
  • 2012-03-05
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
  • 2015-11-05
  • 1970-01-01
相关资源
最近更新 更多