我有一个对象作为成员的类,它没有默认的构造函数。我想在构造函数中初始化这个成员,但似乎在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 :: socket的默认构造函数,根据我的研究,我理解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 ++中的OOP
答案 0 :(得分:8)
定义构造函数时,有两种方法可以“初始化”属性:
如果你没有明确地初始化初始化列表中的一个属性,那么它仍然会被初始化(通过调用它的默认构造函数)...
所以实质上:
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意味着分配没有开销,并且解除引用没有开销(对象是在适当的位置创建的),但却带有正确的语义。
答案 1 :(得分:1)
我认为这是boost::optional的一个可能用例。
答案 2 :(得分:0)
在C ++中,最好初始化初始化列表中的成员,而不是构造函数的主体,所以实际上你可能会考虑将其他成员放在初始化列表中
如果您正在考虑创建其他ctors调用的构造函数,那么直到c ++ 0x(参见inheriting constructors)
才可用答案 3 :(得分:0)
如果要在类的构造函数中构造期间初始化变量,那么正确的方法是:
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)
{
}
编辑:忘记提及'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"))
{
}
答案 4 :(得分:0)
我认为您的解决方案是正确的做事方式。
你也可以通过make is pointer推迟创建对象(但是它改变了代码和数据类型):
std::auto_ptr<udp::socket> _sock;
然后在身体里:
_sock.reset(new udp::soket(_io_service, ep));
但我认为您的“解决方法”是相当正确的解决方案,然后解决方法。
答案 5 :(得分:0)
您可以将_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));
}
答案 6 :(得分:0)
在这种情况下,另一个选择是通过创建一个静态函数来构建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))
{
}
答案 7 :(得分:0)
这是一个非常古老的线程,但还有另一种使用匿名联合的可能性。让我先声明一个没有标准构造函数的辅助类,它会告诉我们构造函数和析构函数调用的时刻:
#include <iostream>
#include <string>
struct Embedded
{
Embedded(const char* init_name)
: name(init_name)
{
std::cout << "Constructor of Embedded: " << name << std::endl;
}
~Embedded()
{
std::cout << "Destructor of Embedded: " << name << std::endl;
}
std::string name;
};
现在让我们将这个类的三个不同对象嵌入到另一个类中。一个放在联合内(不会自动初始化)。对于这个对象,必须手动调用构造函数和析构函数(使用placement new)。这可以在代码中的任何位置进行。
struct Demo
{
Embedded object_1 {"object_1"};
Embedded object_2;
union // members won't be automatically constructed
{
Embedded object_3; // don't use standard initializer (as for object_1)
};
Demo()
: object_2("object_2")
{
std::cout << "Constructor of Demo" << std::endl;
new (&object_3) Embedded ("object_3");
}
~Demo()
{
object_3.~Embedded();
std::cout << "Destructor of Demo" << std::endl;
}
};
最后,在自己的范围内使用 Demo 类:
int main()
{
std::cout << "main() start" << std::endl;
{
Demo demo;
std::cout << "demo created" << std::endl;
}
std::cout << "main() end" << std::endl;
return 0;
}
从输出中可以看出,构造函数被延迟了。
main() start
Constructor of Embedded: object_1
Constructor of Embedded: object_2
Constructor of Demo
Constructor of Embedded: object_3
demo created
Destructor of Embedded: object_3
Destructor of Demo
Destructor of Embedded: object_2
Destructor of Embedded: object_1
main() end
因此您可以准确定义延迟构造函数调用的时刻。缺点是,您还必须手动调用析构函数,否则可能会发生非常糟糕的事情。