使用“:”符号在C ++中定义构造函数

时间:2014-05-06 11:34:34

标签: c++ oop

我正在从结构C转向OOP C ++,我经常在C ++中声明/定义构造函数时,特别使用“:”符号作为运算符。我粗略地理解了这种风格的使用,但有人用这个构造函数定义向我解释了精确的编程技术。

例如:1

class time_stamp
{
public:
    time_stamp(time &t_time)
        : m_time(t_time)
    {}

    ~time_stamp()
    {
        m_time.update(); // as soon as I'm destroyed, update the time
    }
private:
    time &m_time;
};

例如:2

class threaded_class
{
public:
    threaded_class()
        : m_stoprequested(false), m_running(false)
    {
        pthread_mutex_init(&m_mutex);
    }

    ~threaded_class()
    {
        pthread_mutex_destroy(&m_mutex);
    }

    /** Some other member declarations */

}

请在以下2个示例的下面代码行中解释我使用“:time_stamp(time &t_time) : m_time(t_time){}

threaded_class(): m_stoprequested(false), m_running(false)
{
   pthread_mutex_init(&m_mutex);
}

3 个答案:

答案 0 :(得分:7)

冒号字符:用于表示构造函数成员初始化列表。在这里,您可以初始化类的成员或调用基类构造函数。

C ++标准版n3337 12.6.2 § 3:

  

mem-initializer-list可以使用any初始化基类   class-or-decltype,表示基类类型。

C ++标准版n3337 12.6.2 § 7:

  

使用mem-initializer中的expression-list或braced-init-list   初始化指定的子对象(或者,如果是   委托构造函数,完成类对象)   用于直接初始化的8.5的初始化规则。

示例:

class Foo {
   int a;
};

如果你希望整数a在调用构造函数后得到确定的值,你必须在构造函数中给出a这个值。有两种选择:

    构造函数体中的
  • Foo::Foo() {
        a = 70;
    }
    
  • 在其成员初始化列表

    Foo::Foo() : a( 70) {
    }
    

应优先通过成员初始化列表进行初始化

始终合法,永远不会低于构造函数体内的赋值效率,并且通常效率更高。关于初始化列表非常重要的事情是它允许指导初始化类成员,省略受此类过程影响的成员的默认构造。

正如Scott Myers在他的“Effective C ++”中指出的那样,如果你没有为类成员指定一个初始化参数,那么它的默认构造函数将被调用。当您稍后在类构造函数中对其执行赋值时,您将在成员变量上调用operator=。这将对成员函数进行两次调用:一次用于默认构造函数,另一次用于赋值。您可以通过指定初始值设定项来省略第一个调用。正如Scott Myers在他的“Effective C ++”中指出的那样:“从纯粹实用的角度来看,有时候必须使用初始化列表。特别是const和参考成员只能初始化,永远不会分配”。

陷阱

(至少)同样重要的是,成员不会按照它们在初始化列表中的出现顺序进行初始化,而是按类中的声明顺序进行初始化。请记住这一点,以避免错误,如

/* trying to allocate very large block of memory
   as a result of initializing a vector with
   uninitialized integer: std::vector<int> v( N)
*/
class SearchEngine {
    std::vector<int> v;
    int N;
    explicit SearchEngine( std::vector<int> const& keys)
                  : N( keys.size()), v( N), {

C ++标准版n3337 8.5.4 § 1:

  

列表初始化是从a初始化对象或引用   支撑-初始化列表。这样的初始化程序称为初始化程序列表,   和列表中逗号分隔的初始化子句称为   初始化列表的元素。初始化列表可以为空。   列表初始化可以在直接初始化或复制中进行 -   初始化上下文;列表初始化在   直接初始化上下文称为直接列表初始化和   调用复制初始化上下文中的列表初始化   副本列表初始化。 [注意:可以使用列表初始化 - 作为   变量定义中的初始值设定项(8.5)

     

- 作为初始化者   一个新的表达式(5.3.4)

     

- 在一份退货声明(6.6.3)

中      

- 作为一个   函数参数(5.2.2)

     

- 作为下标(5.2.1)

     

- 作为一个论据   构造函数调用(8.5,5.2.3)

     

- 作为a的初始化程序   非静态数据成员(9.2)

     

- 在mem-initializer(12.6.2)

中      

- 关于   作业的右侧(5.17)

     

[例如:

     

int a = {1};

     

std :: complex z {1,2};

     

new std :: vector {“once”,   “曾几何时”}; // 4个字符串元素

     

f({“Nicholas”,“Annemarie”}   ); //传递两个元素的列表

     

return {“Norah”}; //返回列表   一个元素

     

int * e {}; //初始化为零/空指针

     

x =   双{1}; //显式构造一个双

     

的std ::地图   anim = {{“bear”,4},{“cassowary”,2},{“tiger”,7}};

     

- 结束示例] - 结束注释]

答案 1 :(得分:0)

初始化列表。当您想要在构造之后立即初始化成员对象时,它非常有用。当成员对象不是默认的构造函数时,你必须使用它。

它不仅仅是初始化成员的另一种方式,有时你必须使用它,而且大多数时候你应该使用它来保持代码的一致性。

以下是您必须使用它的情况示例:

struct A
{
   const X x; // X has not default constructor

   A() : x(some_value) {}
};

即使成员对象具有默认构造函数,也应该通过initialization-list对其进行初始化,以避免重复构造。

struct A
{
   string x;

   A() : x("Hello") {}
};

在上述情况下,如果您在构造函数的正文中指定"Hello",那么您对string::string()然后x = "Hello";进行了不必要的调用,可以将其替换只需拨打一次string::string("Hello")即可。

答案 2 :(得分:0)

这是成员初始化。这是唯一可以初始化成员的初始成员。

如果在大括号内执行此操作,则已调用成员的默认构造函数并为其分配新值。使用冒号语法,您可以决定成员的初始化方式(根据值fortrivial类型和构造函数方面的非常规类型)。