非指针类成员:它有多好?



我有一个非指针类成员,我需要在构造函数中初始化:

class Alerter {
protected:
Timer timer;
public:
Alerter(int interval);
};

然后

Alerter::Alerter(int interval) {
timer = createTimer(interval);
}

(简化代码只是为了演示问题(。

我有一些疑问和担忧,timer可能首先使用其无参数构造函数创建,后来该实例被createTimer函数返回的内容覆盖。

这种方法有多好?可能的答案可能是:

  • 由其无参数构造函数创建的"空计时器"实际上并没有创建,因为编译器足够聪明,可以发现我们从未创建过 在覆盖值之前引用了它。
  • 空计时器已创建,但这没关系,因为编写良好的代码应该支持一次性实例的廉价无参数构造函数。
  • 最好
  • 使用指针。

这些假设(或者其他假设(中哪一个是最正确的?

timer首先默认构造,然后分配。当然,您可以假设默认构造或编译器优化Timer有多便宜,但在这里您不需要这样做,因为可以使用初始值设定项列表来防止默认构造:

Alerter::Alerter(int interval) : timer(createTimer(interval)) { }

这将起作用,除非您的Timer类是可复制分配的,但不能复制构造,这会很奇怪。

两个构造函数都将被调用(然后也调用赋值(。

我已经扩展了您的示例:

class Timer
{
public:
Timer() : m_interval(0)
{
std::cout << "Constructor withOUT parameter has been called." << std::endl;
}
Timer(int interval) : m_interval(interval)
{
std::cout << "Constructor with parameter has been called." << std::endl;
}
Timer(const Timer& timer)
{
std::cout << "Copy constructor has been called." << std::endl;
m_interval = timer.m_interval;
}
Timer(Timer&& timer)
{
std::cout << "Move constructor has been called." << std::endl;
m_interval = timer.m_interval;
}
void operator=(Timer&& timer)
{
std::cout << "Move assignment has been called." << std::endl;
m_interval = timer.m_interval;
}
private:
int m_interval;
};
class Alerter
{
protected:
Timer timer;
public:
Alerter(int interval)
{
timer = Timer(interval);
}
};
... 
Alerter alerter(12);
...

输出:

Constructor withOUT parameter has been called.
Constructor with parameter has been called.
Move assignment has been called.

并回答您的问题。成员变量最好只初始化一次,但这不是硬性规定。例如,如果 Timer 是一个非常复杂的对象,但默认构造函数只创建一个虚拟对象,则它不会太昂贵且可以接受。

所以,如果你能避免,最好避免。使用指针是一种可能的解决方案,但请注意以下事项:

  • 最好使用std::unique_ptr来避免内存泄漏。
  • 这肯定需要堆内存分配操作。如果对象无论如何都在堆上(例如,使用复杂的内部结构,如 STL 容器(,这不是问题。但是,如果对象非常简单,并且主要用例是将它们创建为本地对象,则可能会导致相当大的性能损失。这应该是一个有意识的设计决策。

最新更新