是否有可能将成员初始化推迟到构造函数体?
我有一个对象作为没有默认构造函数的成员的类。我想在构造函数中初始化这个成员,但似乎在C++中我无法做到这一点。这里是类:是否有可能将成员初始化推迟到构造函数体?
#include <boost/asio.hpp>
#include <boost/array.hpp>
using boost::asio::ip::udp;
template<class T>
class udp_sock
{
public:
udp_sock(std::string host, unsigned short port);
private:
boost::asio::io_service _io_service;
udp::socket _sock;
boost::array<T,256> _buf;
};
template<class T>
udp_sock<T>::udp_sock(std::string host = "localhost",
unsigned short port = 50000)
{
udp::resolver res(_io_service);
udp::resolver::query query(udp::v4(), host, "spec");
udp::endpoint ep = *res.resolve(query);
ep.port(port);
_sock(_io_service, ep);
}
编译器告诉我,基本上,它找不到UDP ::插座一个默认的构造,并通过我的研究,我了解C++隐式调用构造函数之前,每一个成员初始化。有没有办法以我想要的方式去做,或者它是否太“面向Java”,在C++中不可行?
我工作围绕这一问题通过定义这样我的构造函数:
template<class T>
udp_sock<T>::udp_sock(std::string host = "localhost",
unsigned short port = 50000) : _sock(_io_service)
{
udp::resolver res(_io_service);
udp::resolver::query query(udp::v4(), host, "spec");
udp::endpoint ep = *res.resolve(query);
ep.port(port);
_sock.bind(ep);
}
所以我的问题是更多的是出于好奇和更好地理解面向对象的C++
当你定义一个构造函数,你有2种方式 “初始化” 属性:
- 初始化列表
- 的构造体
如果你没有exp在初始化器列表中初始化其中一个属性,然后初始化它(通过调用它的默认构造器)给你...
因此,在本质:
class Example
{
public:
Example();
private:
Bar mAttr;
};
// You write
Example::Example() {}
// The compiler understands
Example::Example(): mAttr() {}
这当然如果基础类型不具有默认构造函数失败。
推迟初始化有多种方法。的“标准”方法是使用一个指针:
class Example { public: Example(); private: Bar* mAttr; };
但是我更喜欢使用Boost.Optional结合合适的存取:
class Example
{
public: Example();
private:
Bar& accessAttr() { return *mAttr; }
const Bar& getAttr() const { return *mAttr; }
boost::Optional<Bar> mAttr;
};
Example::Example() { mAttr = Bar(42); }
因为Boost.Optional意味着有该分配没有开销和在解引用(对象创建就位)上没有任何开销,但却带有正确的语义。
感谢您使用boost :: optional的快速示例,它为我节省了阅读文档所需的时间,以了解它是否可以帮助我或不。这正是我想到的! – Kjir 2010-03-17 17:44:54
当你找到一段时间时,不要犹豫,阅读它。它实际上很短,因为它是一个简单的实用程序,但有一些选项(如就地工厂来构建对象)值得检查。 – 2010-03-18 07:16:47
我认为这是一个可能的使用案例boost::optional。
在写我的例子的时候打我:p这是我处理这种情况的首选方法。 – 2010-03-17 17:26:25
这是我寻找的解决方案,但我选择Matthieu的答案,因为他还提供了一个简单的用例。 – Kjir 2010-03-17 17:49:04
在C++中,它是最好初始化成员初始化列表,而不是在构造函数体,所以实际上你可能会考虑把其他成员在初始化列表
如果你想创建一个构造其他构建函数调用,这是不可直到的C++ 0x(见inheriting constructors)
如果它的初始化施工过程中的变量在类的构造函数的正确方法是:
template<class T>
udp_sock<T>::udp_sock(std::string host = "localhost", unsigned short port = 50000)
:res(_io_service)
,query(udp::v4(), host, "spec")
,ep(*res.resolve(query))
,_sock(_io_service, ep)
{
}
艾迪t:忘了提及'res','query'和'ep'应该是班级的一部分。另一种粗略的方法(没有_sock作为指针)如下:
template<class T>
udp_sock<T>::udp_sock(std::string host = "localhost", unsigned short port = 50000)
:_sock(_io_service, udp::resolver(_io_service).resolve(udp::resolver::query(udp::v4(),host,"spec"))
{
}
但是这需要制作res,query和ep类成员而不是构造函数的局部变量,这会改变他的类的内容,并且不是特别优雅。 – 2010-03-17 17:28:30
我忘记提及了。我已经添加到编辑中,并添加了另一个构造函数,它不需要任何这些变量作为成员变量。它们在运行时创建,一旦使用就会被销毁。使用此方法的主要缺点是在施工期间发生的异常会导致内存泄漏。最好的方法是使用_sock作为指针或boost :: scoped_ptr。 – 2010-03-17 17:33:57
我自己想过第二个版本,但是我发现它没有我想要的那么优雅,所以我一直在寻找更好的解决方案。 – Kjir 2010-03-17 17:40:08
我认为你的解决方案是正确的做事方式。
您也可以通过使推迟对象的创建指针(但是它改变了代码和数据类型):
std::auto_ptr<udp::socket> _sock;
然后在身体:
_sock.reset(new udp::soket(_io_service, ep));
但我认为您的“解决方法”是相当正确的解决方案,然后解决方法。
我打算建议一样的。但是,而不是std :: auto_ptr我会建议boost :: scoped_ptr,因为他已经在使用boost库。 – 2010-03-17 17:25:31
指针意味着堆使用开销和螺旋复制语义......尽管'scoped_ptr'至少会暴露复制问题! – 2010-03-17 17:28:02
我只是希望尽可能使用标准库,特别是当'scoped_ptr'只是“一点点”剥离'auto_ptr'时。我从来没有用过。 – Artyom 2010-03-17 17:28:15
你可以打开_sock
件冲进smart pointer:
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/scoped_ptr.hpp>
using boost::asio::ip::udp;
template<class T>
class udp_sock
{
public:
udp_sock(std::string host, unsigned short port);
private:
boost::asio::io_service _io_service;
boost::scoped_ptr<udp::socket> _sock_ptr;
boost::array<T,256> _buf;
};
template<class T>
udp_sock<T>::udp_sock(std::string host = "localhost",
unsigned short port = 50000)
{
udp::resolver res(_io_service);
udp::resolver::query query(udp::v4(), host, "spec");
udp::endpoint ep = *res.resolve(query);
ep.port(port);
_sock_ptr.reset(new udp::socket(_io_service, ep));
}
这意味着堆分配+复制语义问题。这是很多伤害。 – 2010-03-17 17:27:02
与他的情况不同,因为'boost :: asio :: io_service'已经不可复制。 – 2010-03-17 17:30:41
我也想过指针,但我想避免处理分配/释放以及与指针相关的所有潜在问题。我试图避免指针,除非它们是唯一的解决方案,或者它们提供了明确的速度优势... – Kjir 2010-03-17 17:41:53
在这种情况下,另一种选择是通过创建一个静态函数建立EP来解决此问题:
#include <boost/asio.hpp>
#include <boost/array.hpp>
using boost::asio::ip::udp;
template<class T>
class udp_sock
{
public:
udp_sock(std::string host, unsigned short port);
private:
static udp::endpoint build_ep(const std::string &host,
unsigned short port, boost::asio::io_service &io_service);
boost::asio::io_service _io_service;
udp::socket _sock;
boost::array<T,256> _buf;
};
template<class T>
udp::endpoint udp_sock<T>::build_ep(const std::string &host,
unsigned short port, boost::asio::io_service &io_service)
{
udp::resolver res(io_service);
udp::resolver::query query(udp::v4(), host, "spec");
udp::endpoint ep = *res.resolve(query);
ep.port(port);
return ep;
}
template<class T>
udp_sock<T>::udp_sock(std::string host = "localhost",
unsigned short port = 50000)
: _sock(_io_service, build_ep(host, port, _io_service))
{
}
或者您可以驱逐在外面的功能在体内产生的计算:'udp_sock ():_sock(myfunc()){}' – 2010-03-17 17:29:25