代码之家  ›  专栏  ›  技术社区  ›  Audrius MeÅ¡kauskas

非指针类成员:有多好?[副本]

  •  6
  • Audrius MeÅ¡kauskas  · 技术社区  · 6 年前

    这个问题已经有了答案:

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

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

    然后

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

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

    我有些怀疑和担心 timer 可能是首先使用其无参数构造函数创建的,然后该实例将被 createTimer 函数返回。

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

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

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

    2 回复  |  直到 6 年前
        1
  •  11
  •   Holt 111111    6 年前

    timer 是先默认构造然后分配的。当然你可以假设 Timer 是默认构造或关于编译器优化,但这里不需要这样做,因为可以使用初始值设定项列表阻止默认构造:

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

    这会有用的,除非你 计时器 类是可复制可分配的,但不是可复制可构造的,这会很奇怪。

        2
  •  3
  •   Tibor Takács    6 年前

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

    我扩展了你的例子:

    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.
    

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

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