2010-03-17 3 views
3

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

+0

В качестве альтернативы вы можете быть в состоянии депортировать вычисление происходящего в организме во внешней функции: 'udp_sock(): _sock (myfunc()) {} ' –

ответ

8

При определении конструктора, у вас есть 2 способа "Initialize" атрибуты:

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

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

Таким образом, в сущности:

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 означает, что нет никаких накладных расходов на распределении и нет накладных расходов на разыменование (объект создается на месте) и все же несет правильную семантику.

+0

Спасибо за быстрый пример с использованием boost :: optional, он спас мне время, необходимое для чтения документации, чтобы понять, может ли это помочь мне или нет. Это именно то, что я имел в виду! – Kjir

+0

Не стесняйтесь читать его, когда вы находите какое-то время. Это на самом деле довольно коротко, так как это простая утилита, но есть некоторые опции (например, фабрика на месте для сборки объекта), которые стоит проверить. –

1

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

+0

Ударьте меня, пока я пишу свои примеры: p Это мой предпочтительный способ справиться с этим делом. –

+0

Это то решение, которое я искал, но я выбираю ответ Маттиу, так как он также предоставил простой вариант использования. – Kjir

0

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

Если вы подумываете о создании конструктора что другие ctors называют, что не доступно сезам C++ 0x (см inheriting constructors)

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) 
{ 
} 

Edi 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")) 
{ 
} 
+0

Но для этого требуются члены 'res',' query' и 'ep', а не локальные переменные конструктора, что изменяет содержимое его класса и не особенно элегантно. –

+0

Я забыл упомянуть об этом. Я добавил это в редактирование и добавил другой конструктор, который не нуждается ни в одной из этих переменных в качестве переменной-члена. Они создаются во время выполнения и уничтожаются, как только они используются. Основным недостатком использования этого метода является то, что исключение, которое возникает во время строительства, приведет к утечке памяти. Лучший способ - либо использовать _sock как указатель, либо boost :: scoped_ptr. –

+0

Я подумал о второй версии самостоятельно, но я считаю ее не такой элегантной, как хотелось бы, поэтому я продолжал искать лучшие решения. – Kjir

0

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

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

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

И тогда в теле:

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

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

+0

Я собирался предложить то же самое. Но вместо std :: auto_ptr я собирался предложить boost :: scoped_ptr, так как он уже использует расширенные libararies. –

+0

Указатели означают использование кучи накладных расходов и привинченную семантику копирования ... хотя 'scoped_ptr' будет хотя бы разоблачать проблемы с копией! –

+0

Я просто предпочитаю использовать стандартную библиотеку, где это возможно, особенно когда 'scoped_ptr' просто« немного »лишен' auto_ptr'. Я никогда не использую его. – Artyom

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)); 
} 
+0

Это означает выделение кучи + проблемы семантики копирования. Это очень больно. –

+0

Не так много в его случае, поскольку 'boost :: asio :: io_service' уже не копируется. –

+0

Я тоже думал о указателях, но я не хотел иметь дело с распределением/освобождением и всеми потенциальными проблемами, связанными с указателями. Я стараюсь избегать указателей, если только они не являются единственным решением или обеспечивают четкие преимущества скорости ... – Kjir

0

Другим вариантом в этом случае является работой по вопросу создания статической функции для построения ера:

#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)) 
{ 
} 
Смежные вопросы