我正在编写一个C ++ websocket服务器库。在我提供的一个示例中,我使用了两个类session_base
和session
。我这样做是为了在tcp::socket
父类中初始化session_base
对象(使用移动构造函数),然后将对它的引用传递给存储此引用的ws::session<tcp::socket>
父类供以后使用。我创建ws:session作为模板类的原因是我可以使用boost::asio::ssl::stream
以及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";
}
};
答案 0 :(得分:1)
如果ws::session
构造函数仅存储对套接字的引用但不使用引用,例如,调用套接字成员函数或访问数据成员,则程序格式正确。该标准将此称为3.8 / 6([basic.life/6])中分配的存储的引用:
类似地,在对象的生命周期开始之前但是在对象将占用的存储之后已经分配,或者在对象的生命周期结束之后以及在对象的存储之前 被占用被重用或释放,任何引用原始对象的glvalue都可以使用,但只能以有限的方式使用。对于正在建造或销毁的物体,见12.7。否则,这样的glvalue指的是已分配的存储(3.7.4.2),并且使用不依赖于其值的glvalue的属性是明确定义的。如果出现以下情况,该程序具有未定义的行为:
- 左值到右值的转换(4.1)应用于这样的glvalue,
- glvalue用于访问非静态数据成员或调用对象的非静态成员函数,或
- glvalue被隐式转换(4.10)为对基类类型的引用,或
- glvalue用作
static_cast
(5.2.9)的操作数,除非转换最终是 cvchar&
或 cvunsigned char&
或- glvalue用作
dynamic_cast
(5.2.7)的操作数或typeid
的操作数。
请注意,必须在获取引用时分配存储,并且在调用session
构造函数时也是如此。
但尽管我不推荐这种方法。主要是因为在ws::session
构造函数中很容易忘记传递的引用引用了一个尚未初始化的对象并在以后引入了一个bug。最好使用base-from-member idiom并保留原始代码。