C++ 变量初始化方式总结 | 拷贝初始化 | 列表初始化 | 值初始化

发布于:2025-08-06 ⋅ 阅读:(12) ⋅ 点赞:(0)

1. 拷贝初始化

int a = 1;
std::string s = "hello";
  • 最经典、最普遍的写法

  • 类类型会调用构造函数进行“拷贝构造”(可能优化为直接构造)

  • 存在隐式类型转换风险

2. 直接初始化

int a(1);
std::string s("hello");
  • 使用括号直接初始化变量

  • 对类对象来说通常更高效

  • 构造函数如果加了 explicit,只能使用此方式

3. 列表初始化

示例:

int a{10};                 // 初始化为 10
std::string s{"hello"};    // 初始化字符串
std::vector<int> v{1, 2, 3}; // 初始化容器

列表初始化的几种形式

写法 名称 示例
T var{val}; 直接列表初始化 int a{10};
T var = {val}; 拷贝列表初始化 int b = {20};
T var{}; 值初始化(默认) int c{}; // 0

列表初始化的优点

1. 防止窄化转换

int a{3.14};  // ❌ 编译错误:不能将 double 隐式转换为 int
int b = 3.14; // ✅ 会自动截断为 3(但可能有隐患)

所以 {} 更安全,防止精度丢失

2. 支持类类型初始化

class Person {
public:
    Person(int age, std::string name) { ... }
};

Person p1{18, "Tom"};  // 列表初始化成员变量
  • 简洁、清晰

  • 支持构造函数匹配

3. 可以初始化数组、容器等聚合类型

int arr[] = {1, 2, 3};                 // 数组
std::vector<int> vec{1, 2, 3};        // 容器
std::tuple<int, std::string>{1, "a"}; // 其他聚合结构

4. 原理:列表初始化本质调用的是 initializer_list 构造函数

当你使用花括号 {} 来初始化一个类对象时,编译器会尝试匹配下面这些可能的构造函数:

  1. 最优先:MyArray(std::initializer_list<T>)

  2. 然后尝试普通构造函数:MyArray(int, int)

所以,要让你的 MyArray<T> 支持:

MyArray<int> arr{1, 2, 3};

你必须提供:

MyArray(std::initializer_list<T> list)

示例:手写一个支持列表初始化的 MyArray 类模板

#include <iostream>
#include <initializer_list>

template<typename T>
class MyArray {
private:
    T* pData;
    std::size_t m_Size;

public:
    // 构造函数:支持 std::initializer_list
    MyArray(std::initializer_list<T> list)
        : pData(new T[list.size()]),
          m_Size(list.size()) 
    {
        std::size_t i = 0;
        for (const auto& item : list) 
        {
            pData[i++] = item;
        }
        std::cout << "initializer_list 构造函数调用" << std::endl;
    }

    // 析构函数
    ~MyArray() {
        delete[] pData;
    }

    // 打印函数
    void print() const {
        for (std::size_t i = 0; i < m_Size; ++i) {
            std::cout << pData[i] << " ";
        }
        std::cout << std::endl;
    }
};

  使用示例

int main() {
    MyArray<int> arr{10, 20, 30, 40};
    arr.print();  // 输出:10 20 30 40
    return 0;
}

解析:std::initializer_list<T> 是什么?

  • 它是一个 轻量容器,用于存储一系列值。

  • 实际上,它内部只是一个指向数组的指针 + 元素个数。

  • 提供 .begin().end() 迭代器,可以用于范围 for 循环。

std::initializer_list<int> list = {1, 2, 3};
for (int v : list) {
    std::cout << v << std::endl;
}

注意事项

项目 说明
只能作为构造函数参数 initializer_list 不能直接赋值,只能构造或拷贝
只能接受同一类型元素 {1, 2, 3} 中必须都是同一种类型
列表初始化优先匹配 initializer_list 构造函数 如果存在多个构造函数,花括号会优先调用这个

为了更强大,一般会重载多个构造函数:

MyArray(std::size_t size);                 // 有参构造
MyArray();                                 // 默认构造
MyArray(std::initializer_list<T> list);   // 列表初始化

这样你还可以支持:

MyArray<int> a1;               // 默认构造
MyArray<int> a2(10);           // 分配10个元素
MyArray<int> a3{1, 2, 3, 4};   // 列表初始化

    4. 拷贝列表初始化

    int a = {10};
    std::string s = {"abc"};
    
    • 使用 = + {} 的组合

    • 本质与列表初始化类似,禁止窄化

    5. 值初始化

    int a{};            // a 为 0
    std::string s{};    // s 是空字符串
    
    • 默认初始化为“零值”

    • 不再有“随机值”问题

    6. 默认初始化

    int a;  // ❌ 值未定义,可能是随机数
    
    • 局部变量如果未初始化,其值是“野值”

    • 对象成员变量如果没初始化,可能会调用默认构造函数(如果存在)

    数组、结构体、指针的初始化方式

    数组初始化

    int arr[3] = {1, 2};  // arr[2] 自动初始化为 0
    int arr[3]{};         // 所有元素初始化为 0
    

    指针初始化

    int* p1 = nullptr;  // 推荐写法(C++11)
    int* p2 = 0;        // 旧写法(仍合法,但不推荐)
    

     结构体初始化

    struct Point { int x, y; };
    Point p1 = {1, 2};  // C++98 风格
    Point p2{3, 4};     // C++11 风格,推荐
    

    类初始化与构造函数调用的关系

    class Person {
    public:
        explicit Person(int age) { ... }
    };
    
    Person p1 = 18;  // ❌ 错误:explicit 构造函数不能用拷贝初始化
    Person p2(18);   // ✅ 直接初始化
    Person p3{18};   // ✅ 列表初始化(推荐)
    

    explicit 构造函数:禁止隐式转换,因此无法使用 = 赋值初始化,只能用括号或花括号


    网站公告

    今日签到

    点亮在社区的每一天
    去签到