是否可以将成员初始化推迟到构造函数体?

时间:2010-03-17 17:07:11

标签: c++ oop boost constructor initialization

我有一个对象作为成员的类,它没有默认的构造函数。我想在构造函数中初始化这个成员,但似乎在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

8 个答案:

答案 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

因此您可以准确定义延迟构造函数调用的时刻。缺点是,您还必须手动调用析构函数,否则可能会发生非常糟糕的事情。