C++冷知识:构造函数初始化时,为什么使用 : 而不是使用作用域内初始化对象?

简介: 在这个例子中,初始化列表中的成员变量顺序与类定义中的顺序不一致,可能会导致未定义的行为。如果成员变量是const或引用类型,必须在初始化列表中进行初始化,否则会导致编译错误。

:是什么?

这样的行为被称之为初始化列表。具体展示如下:


直接初始化对象。

以一个线程池类为例:


class ThreadPool
{
public:
    // 构造函数,创建指定数量的线程
    ThreadPool(size_t num_threads) : stop(false)
    {
        ....
    }
    // 析构函数,销毁线程池
    ~ThreadPool()
    {
        ...
    }
private:
    ...
    bool stop;       // 线程池是否被销毁的标志
};



传入值初始化对象

当初始化队列包含多个对象时,以,间隔

class ThreadPool
{
public:
    // 构造函数,创建指定数量的线程
    ThreadPool(size_t num_threads, bool flag) : stop(false), flag(flag)
    {
        ....
    }
    // 析构函数,销毁线程池
    ~ThreadPool()
    {
        ...
    }
private:
    ...
    bool stop;       // 线程池是否被销毁的标志
    bool flag;
};


初始化列表初始化复杂数据结构

使用初始化列表可以在创建对象时直接初始化复杂数据结构的成员变量,避免了在构造函数体中手动初始化的复杂过程。这样可以提高代码的效率和可读性,减少代码的执行时间和内存占用。因此,在使用复杂数据结构时,使用初始化列表是一个合适的选择。例如,如果一个类的成员变量是一个vector,可以使用初始化列表来初始化它:


class MyClass {
public:
    MyClass() : my_vector{1, 2, 3, 4, 5} {}
private:
    std::vector<int> my_vector;
};


这样就可以在创建对象时直接初始化my_vector,避免了手动添加元素的复杂过程。


两者的不同有哪些

初始化列表和作用域内初始化都可以用来初始化对象的成员变量,但它们的实现方式和效果有所不同。


使用初始化列表可以在对象创建时直接初始化成员变量,避免了不必要的中间步骤。这样可以提高代码的效率和可读性,减少代码的执行时间和内存占用。此外,使用初始化列表还可以避免一些编译器优化问题,使代码更加稳定和可靠。


而在作用域内初始化对象会在构造函数体中执行,需要额外的代码来初始化成员变量。这样会增加代码的复杂度和执行时间,降低代码的可读性和效率。此外,作用域内初始化可能会导致一些编译器优化问题,使代码不够稳定和可靠。


因此,使用初始化列表比作用域内初始化更加优秀,是更好的选择。


总结

初始化列表,可以自定义初始化、传入值初始化、数据结构初始化。

初始化列表比作用域内赋值更加方便、快捷、稳定。

使用初始化列表时需要注意以下几个问题:


初始化列表中的成员变量顺序应该与类定义中的顺序一致,否则可能会导致未定义的行为。

错误示例:

class ThreadPool {
public:
    ThreadPool(size_t num_threads, bool flag) : flag(flag), stop(false) {};
    // other methods and variables
private:
    bool stop; // 这里定义顺序和初始化时不一致,可能有问题。
    bool flag;
    // other variables
};


在这个例子中,初始化列表中的成员变量顺序与类定义中的顺序不一致,可能会导致未定义的行为。


如果成员变量是const或引用类型,必须在初始化列表中进行初始化,否则会导致编译错误。

错误示例:


class Example {
public:
    Example(int a) {
        const int b = a;
    }
private:
    const int b;
};


在这个例子中,成员变量b是const类型,但是没有在初始化列表中进行初始化,会导致编译错误。


如果成员变量是指针类型,需要注意指针的生命周期,避免出现悬空指针或内存泄漏的问题。

错误示例:


class Example {
public:
    Example() {
        int* p = new int(10);
    }
    ~Example(){}
private:
    int* p;
};


在这个例子中,成员变量p是指针类型,但是没有在析构函数中释放内存,会导致内存泄漏。


如果成员变量是类类型,需要确保该类的构造函数已经被定义和实现,否则会导致编译错误。

错误示例:


class Example {
public:
    Example() {}
private:
    OtherClass other;
};
class OtherClass {
public:
    OtherClass(int a) {}
};



在这个例子中,成员变量other是OtherClass类型,但是OtherClass的构造函数没有被定义和实现,会导致编译错误。


正确示例:


class OtherClass {
public:
    OtherClass(int a) {}
};
class Example {
public:
    Example() : other(0) {}
private:
    OtherClass other;
};class OtherClass {
public:
    OtherClass(int a) {}
};
class Example {
public:
    Example() : other(0) {}
private:
    OtherClass other;
};


如果成员变量是基本类型,可以使用默认值来初始化,避免重复的代码。

错误示例:


class Example {
public:
    Example() {
        a = 0;
        b = 0;
    }
private:
    int a;
    int b;
};


在这个例子中,成员变量a和b可以使用默认值来初始化,避免重复的代码。


正确示例:


class Example {
public:
    Example() : a(0), b(0) {}
private:
    int a;
    int b;
};


总之,使用初始化列表时需要仔细考虑每个成员变量的初始化方式和顺序,确保代码的正确性和可读性。

目录
相关文章
|
2天前
|
编译器 C++
C++ 类构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
43 30
|
17天前
|
C++
C++(十一)对象数组
本文介绍了C++中对象数组的使用方法及其注意事项。通过示例展示了如何定义和初始化对象数组,并解释了栈对象数组与堆对象数组在初始化时的区别。重点强调了构造器设计时应考虑无参构造器的重要性,以及在需要进一步初始化的情况下采用二段式初始化策略的应用场景。
|
17天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)
|
1月前
|
存储 安全 编译器
【C++】类和对象(下)
【C++】类和对象(下)
【C++】类和对象(下)
|
1月前
|
编译器 C++
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
|
1月前
|
编译器 C++
【C++】类和对象(中)
【C++】类和对象(中)
|
1月前
|
存储 编译器 程序员
【C++】类和对象(上)
【C++】类和对象(上)
|
1月前
|
Dart API 开发工具
Dart ffi 使用问题之Dart API要在C++中使用,该如何初始化
Dart ffi 使用问题之Dart API要在C++中使用,该如何初始化
|
17天前
|
C++
C++(十六)类之间转化
在C++中,类之间的转换可以通过转换构造函数和操作符函数实现。转换构造函数是一种单参数构造函数,用于将其他类型转换为本类类型。为了防止不必要的隐式转换,可以使用`explicit`关键字来禁止这种自动转换。此外,还可以通过定义`operator`函数来进行类型转换,该函数无参数且无返回值。下面展示了如何使用这两种方式实现自定义类型的相互转换,并通过示例代码说明了`explicit`关键字的作用。
|
17天前
|
存储 设计模式 编译器
C++(十三) 类的扩展
本文详细介绍了C++中类的各种扩展特性,包括类成员存储、`sizeof`操作符的应用、类成员函数的存储方式及其背后的`this`指针机制。此外,还探讨了`const`修饰符在成员变量和函数中的作用,以及如何通过`static`关键字实现类中的资源共享。文章还介绍了单例模式的设计思路,并讨论了指向类成员(数据成员和函数成员)的指针的使用方法。最后,还讲解了指向静态成员的指针的相关概念和应用示例。通过这些内容,帮助读者更好地理解和掌握C++面向对象编程的核心概念和技术细节。