Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to defer member initialization to the constructor body?

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++

like image 909
Kjir Avatar asked Mar 17 '10 17:03

Kjir


1 Answers

When you define a constructor, you have 2 ways to "initialize" attributes:

  • the initializer list
  • the constructor body

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.

like image 113
Matthieu M. Avatar answered Sep 24 '22 01:09

Matthieu M.