Reputation: 4547
I have a class with an object as a member which doesn't have a default constructor. I'd like to initialize this member in the constructor, but it seems that in C++ I can't do that. Here is the class:
#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);
}
The compiler tells me basically that it can't find a default constructor for udp::socket and by my research I understood that C++ implicitly initializes every member before calling the constructor. Is there any way to do it the way I wanted to do it, or is it too "Java-oriented" and not feasible in C++?
I worked around the problem by defining my constructor like this:
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);
}
So my question is more out of curiosity and to better understand OOP in C++
Upvotes: 6
Views: 7525
Reputation: 126
This is a really ancient thread, but there is another possibility using an anonymous union. Let me first declare a helper class without a standard constructor, which will inform us about the moment of constructor and destructor calling:
#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;
};
Now let us embed three different objects of this class in another class. One is placed inside a union (which won't be automatically initialized). For this object, constructor and destructor must be manually called (using placement new). This is possible at any position in the code.
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;
}
};
Finally, use the Demo class in its own scope:
int main()
{
std::cout << "main() start" << std::endl;
{
Demo demo;
std::cout << "demo created" << std::endl;
}
std::cout << "main() end" << std::endl;
return 0;
}
As you can see from the output, the constructor is deferred.
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
So you can exactly define the moment of the deferred constructor call. The downside is, that you also have to manually call the destructor or really bad things can happen.
Upvotes: 1
Reputation: 985
Another option in this case is to work around the issue by creating a static function to build 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))
{
}
Upvotes: 0
Reputation: 6645
If it's to initialize a variable during construction in a class's constructor the right way is:
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)
{
}
Edit: Forgot to mention that 'res', 'query' and 'ep' should be part of the class. Another crude method (without having _sock as a pointer) is as given below:
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"))
{
}
Upvotes: 0
Reputation: 299930
When you define a constructor, you have 2 ways to "initialize" attributes:
If you do not explictly initialize one of the attributes in the initializer list, it is nonetheless initialized (by calling its default constructor) for you...
So in essence:
class Example
{
public:
Example();
private:
Bar mAttr;
};
// You write
Example::Example() {}
// The compiler understands
Example::Example(): mAttr() {}
And this of course fails if the underlying type does not have a Default Constructor.
There are various ways to defer this initialization. The "standard" way would be to use a pointer:
class Example { public: Example(); private: Bar* mAttr; };
However I prefer using Boost.Optional combined with suitable accessors:
class Example
{
public: Example();
private:
Bar& accessAttr() { return *mAttr; }
const Bar& getAttr() const { return *mAttr; }
boost::Optional<Bar> mAttr;
};
Example::Example() { mAttr = Bar(42); }
Because Boost.Optional means that there is no overhead on the allocation and no overhead on the dereferencing (the object is created in place) and yet carries the correct semantic.
Upvotes: 8
Reputation: 10347
You could turn the _sock
member into a 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));
}
Upvotes: 0
Reputation: 31243
I think your solution is the correct way to do things.
You can also postpone the creation of the object by making is pointer (however it changes the code and data type):
std::auto_ptr<udp::socket> _sock;
And then in body:
_sock.reset(new udp::soket(_io_service, ep));
But I think that your "workaround" is rather correct solution then workaround.
Upvotes: 0
Reputation: 1489
In C++ it's preferable to initialize members in the initializer list, rather than the body of the constructor, so in fact you might consider putting other members in the initialization list
If you're thinking about creating a constructor that other ctors call, that's not available til c++0x (see inheriting constructors)
Upvotes: 0