Можно ли отложить инициализацию члена до тела конструктора? - PullRequest
3 голосов
/ 17 марта 2010

У меня есть класс с объектом в качестве члена, который не имеет конструктора по умолчанию. Я хотел бы инициализировать этот член в конструкторе, но кажется, что в 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 ++

Ответы [ 7 ]

8 голосов
/ 17 марта 2010

Когда вы определяете конструктор, у вас есть 2 способа «инициализировать» атрибуты:

  • список инициализаторов
  • тело конструктора

Если вы явно не инициализируете один из атрибутов в списке инициализаторов, он тем не менее инициализируется (вызывая его конструктор по умолчанию) для вас ...

Итак, по сути:

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 голос
/ 17 марта 2010

Я думаю, что это один из возможных вариантов использования boost :: необязательный .

0 голосов
/ 22 февраля 2012

Другой вариант в этом случае - обойти проблему, создав статическую функцию для сборки 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))
{
}
0 голосов
/ 17 марта 2010

Вы можете превратить элемент _sock в умный указатель :

#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));
}
0 голосов
/ 17 марта 2010

Я думаю, что ваше решение - верный способ сделать что-то.

Вы также можете отложить создание объекта, сделав указатель (однако это изменит код и тип данных):

std::auto_ptr<udp::socket> _sock;

А потом в теле:

_sock.reset(new udp::soket(_io_service, ep));

Но я думаю, что ваш «обходной путь» - это скорее правильное решение, чем обходной.

0 голосов
/ 17 марта 2010

Если это инициализировать переменную во время построения в конструкторе класса, правильный путь:

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"))
{
}
0 голосов
/ 17 марта 2010

В C ++ предпочтительнее инициализировать элементы в списке инициализаторов, а не тело конструктора, поэтому на самом деле вы можете рассмотреть вопрос о включении других членов в список инициализации

Если вы думаете о создании конструктора, который вызывают другие ctors, он недоступен, пока c ++ 0x (см. наследование конструкторов )

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...