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

Use of ":" symbol to define a constructor in C++

本文关键字:定义 构造函数 C++ 符号 使用      更新时间:2023-10-16

我正从结构化C转向面向对象的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 */
}

请解释一下上面两个例子中下面几行代码中":"的用法time_stamp(time &t_time) : m_time(t_time){}

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

冒号:表示构造函数成员初始化列表。这是您可以初始化类成员或调用基类构造函数的地方。

c++标准n3337 12.6.2 § 3:

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

c++标准n3337 12.6.2 § 7:

使用mem初始化器中的表达式列表或带括号的初始化列表初始化指定的子对象(或,如果是委托构造函数(完整的类对象)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和reference成员只能初始化,不能赋值。

(至少)同样重要的是,成员不是按照初始化列表中出现的顺序初始化的,而是按照类中的声明顺序初始化的。记住这一点可以避免像

这样的错误
/* 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:

List-initialization是对对象或引用的初始化braced-init-list。这样的初始化项称为初始化项列表,列表的逗号分隔的初始化子句称为初始化列表的元素。初始化列表可以为空。列表初始化可以在直接初始化或复制中发生初始化上下文;List-initialization in a直接初始化上下文称为直接列表初始化和在复制初始化上下文中调用列表初始化copy-list-initialization。[注:列表初始化可以使用- as。变量定义中的初始化式(8.5)

-作为一个新的表达式(5.3.4)

-在return语句(6.6.3)

- as a函数实参(5.2.2)

-作为下标(5.2.1)

-作为参数构造函数调用(8.5,5.2.3)

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

-在mems初始化式中(12.6.2)

-在赋值操作的右侧(5.17)

[示例:

]

int a = {1};

std::复杂的z {1,2};

新std::向量{"一次","upon", "a", "time"};//4个字符串元素

f({"尼古拉斯"、"安玛丽"});//传递两个元素的列表

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

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

x =双{1};//显式构造双精度对象

std::地图动画={{"熊",4},{"食火鸡",2},{"老虎",7}};

- end note]

用于成员初始化。这是你唯一可以在不被默认初始化的情况下初始化成员的地方。

如果在花括号内这样做,则成员的默认构造函数已经被调用,并且正在为其赋新值。使用冒号语法,您可以决定如何初始化成员(就普通类型的值而言,以及就非普通类型的构造函数而言)。

初始化列表。当您想要在构造之后初始化成员对象时,它很有用。当成员对象具有非默认构造函数时,必须使用它。

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

下面是一个你必须使用它的例子:

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")来代替。