• C++
  • C++ 动态内存分配初始化学习笔记教程

  • @ 2025-8-10 15:07:01

C++ 动态内存分配初始化学习笔记教程

1. 动态内存分配初始化的基本概念

在C++中,使用new操作符进行动态内存分配时,不仅可以申请内存空间,还可以对其进行初始化。动态内存初始化是指在分配内存的同时为其赋予初始值,这一过程能确保内存被正确初始化,避免使用未初始化的内存导致的不可预期行为(如垃圾值、程序崩溃等)。

动态内存初始化的核心价值在于:

  • 保证内存使用的安全性,避免未初始化内存带来的隐患。
  • 简化代码逻辑,将内存分配与初始化合并为一步操作。
  • 支持多种初始化方式,适配不同场景的需求(如基础类型、自定义类型、数组等)。

2. 单个对象的动态内存初始化

对于单个基础类型或自定义类型对象,动态内存初始化主要有以下几种方式:

2.1 基础类型的初始化

(1)值初始化(C++98及以上)

使用圆括号()指定初始值,适用于所有基础类型(intdoublechar等)。

#include <iostream>

int main() {
    // 初始化int类型动态内存
    int* intPtr = new int(10);
    std::cout << "int值: " << *intPtr << std::endl; // 输出:10
    
    // 初始化double类型动态内存
    double* doublePtr = new double(3.14159);
    std::cout << "double值: " << *doublePtr << std::endl; // 输出:3.14159
    
    // 初始化char类型动态内存
    char* charPtr = new char('A');
    std::cout << "char值: " << *charPtr << std::endl; // 输出:A
    
    // 释放内存
    delete intPtr;
    delete doublePtr;
    delete charPtr;
    
    return 0;
}

(2)列表初始化(C++11及以上)

使用花括号{}指定初始值,是C++11引入的更通用的初始化方式,支持基础类型和复杂类型。

#include <iostream>

int main() {
    // 列表初始化int
    int* intPtr = new int{20};
    std::cout << "int值: " << *intPtr << std::endl; // 输出:20
    
    // 列表初始化double
    double* doublePtr = new double{9.8};
    std::cout << "double值: " << *doublePtr << std::endl; // 输出:9.8
    
    // 释放内存
    delete intPtr;
    delete doublePtr;
    
    return 0;
}

(3)零初始化

如果初始化时不指定具体值(仅使用(){}),基础类型会被初始化为0(或等价的零值,如false'\0'等)。

#include <iostream>

int main() {
    // 零初始化int(值为0)
    int* intPtr = new int();
    std::cout << "int零值: " << *intPtr << std::endl; // 输出:0
    
    // 零初始化bool(值为false)
    bool* boolPtr = new bool{};
    std::cout << "bool零值: " << std::boolalpha << *boolPtr << std::endl; // 输出:false
    
    // 零初始化char(值为'\0')
    char* charPtr = new char();
    std::cout << "char零值(ASCII码): " << static_cast<int>(*charPtr) << std::endl; // 输出:0
    
    // 释放内存
    delete intPtr;
    delete boolPtr;
    delete charPtr;
    
    return 0;
}

2.2 自定义类型(结构体/类)的初始化

对于自定义类型(如结构体或类),动态内存初始化会自动调用其构造函数,因此可以通过new操作符传递构造函数所需的参数。

示例:结构体的动态初始化

#include <iostream>
#include <string>

struct Student {
    std::string name;
    int age;
    
    // 构造函数
    Student(std::string n, int a) : name(n), age(a) {}
    
    void display() {
        std::cout << "姓名: " << name << ", 年龄: " << age << std::endl;
    }
};

int main() {
    // 使用圆括号传递构造函数参数
    Student* stu1 = new Student("张三", 18);
    stu1->display(); // 输出:姓名: 张三, 年龄: 18
    
    // 使用花括号传递构造函数参数(C++11及以上)
    Student* stu2 = new Student{"李四", 19};
    stu2->display(); // 输出:姓名: 李四, 年龄: 19
    
    // 释放内存(会自动调用析构函数)
    delete stu1;
    delete stu2;
    
    return 0;
}

示例:带默认构造函数的类初始化

如果类定义了默认构造函数(无参构造函数),动态初始化时可以不传递参数,直接调用默认构造函数。

#include <iostream>
#include <string>

class Book {
private:
    std::string title;
    std::string author;
public:
    // 默认构造函数
    Book() : title("未知书名"), author("未知作者") {}
    
    // 带参数的构造函数
    Book(std::string t, std::string a) : title(t), author(a) {}
    
    void show() {
        std::cout << "书名: " << title << ", 作者: " << author << std::endl;
    }
};

int main() {
    // 调用默认构造函数(不传递参数)
    Book* book1 = new Book();
    book1->show(); // 输出:书名: 未知书名, 作者: 未知作者
    
    // 调用带参数的构造函数
    Book* book2 = new Book{"C++编程", "张三"};
    book2->show(); // 输出:书名: C++编程, 作者: 张三
    
    delete book1;
    delete book2;
    
    return 0;
}

3. 动态数组的初始化

动态数组(使用new[]分配的数组)的初始化方式与单个对象有所不同,主要支持列表初始化和零初始化,且需注意数组元素的类型是否支持相应的初始化方式。

3.1 基础类型数组的初始化

(1)列表初始化(C++11及以上)

使用花括号{}为数组元素指定初始值,元素数量可以少于数组大小(剩余元素会被零初始化)。

#include <iostream>

int main() {
    // 初始化int数组(指定所有元素)
    int* arr1 = new int[3]{1, 2, 3};
    std::cout << "arr1: ";
    for (int i = 0; i < 3; i++) {
        std::cout << arr1[i] << " "; // 输出:1 2 3
    }
    std::cout << std::endl;
    
    // 初始化double数组(元素数量少于数组大小,剩余元素零初始化)
    double* arr2 = new double[5]{3.14, 2.71};
    std::cout << "arr2: ";
    for (int i = 0; i < 5; i++) {
        std::cout << arr2[i] << " "; // 输出:3.14 2.71 0 0 0
    }
    std::cout << std::endl;
    
    // 释放数组内存
    delete[] arr1;
    delete[] arr2;
    
    return 0;
}

(2)零初始化

如果仅使用(){}而不指定具体值,数组的所有元素都会被零初始化。

#include <iostream>

int main() {
    // 使用()零初始化int数组
    int* arr3 = new int[4]();
    std::cout << "arr3: ";
    for (int i = 0; i < 4; i++) {
        std::cout << arr3[i] << " "; // 输出:0 0 0 0
    }
    std::cout << std::endl;
    
    // 使用{}零初始化char数组
    char* arr4 = new char[3]{};
    std::cout << "arr4(ASCII码): ";
    for (int i = 0; i < 3; i++) {
        std::cout << static_cast<int>(arr4[i]) << " "; // 输出:0 0 0
    }
    std::cout << std::endl;
    
    delete[] arr3;
    delete[] arr4;
    
    return 0;
}

3.2 自定义类型数组的初始化

自定义类型数组的初始化会为每个元素调用相应的构造函数,因此需要确保类/结构体有匹配的构造函数。

示例:带构造函数的结构体数组初始化

#include <iostream>
#include <string>

struct Point {
    int x;
    int y;
    
    // 带参数的构造函数
    Point(int a, int b) : x(a), y(b) {}
    
    // 默认构造函数(用于零初始化或未指定参数的元素)
    Point() : x(0), y(0) {}
};

int main() {
    // 初始化Point数组(指定部分元素,剩余元素调用默认构造函数)
    Point* points = new Point[4]{Point(1, 2), Point(3, 4)};
    
    std::cout << "Points数组: " << std::endl;
    for (int i = 0; i < 4; i++) {
        std::cout << "(" << points[i].x << ", " << points[i].y << ")" << std::endl;
    }
    // 输出:
    // (1, 2)
    // (3, 4)
    // (0, 0)  // 未指定,调用默认构造
    // (0, 0)  // 未指定,调用默认构造
    
    delete[] points;
    return 0;
}

注意:

  • 如果自定义类型没有默认构造函数,初始化数组时必须为所有元素指定参数,否则会编译报错。
  • 例如,若Point结构体没有默认构造函数,new Point[4]{Point(1,2)}会报错(因为后3个元素无法初始化)。

4. 动态内存初始化的常见问题与注意事项

4.1 初始化方式的兼容性

  • 圆括号初始化(new T(val))在C++98中就已支持,但不支持数组的列表初始化。
  • 花括号初始化(new T{val})是C++11引入的统一初始化语法,支持单个对象和数组,且能避免“最令人头疼的解析”(如new int()new int的歧义)。
  • 建议在C++11及以上版本中优先使用花括号初始化,提高代码一致性和安全性。

4.2 避免过度初始化

  • 对于大型数组,若后续会立即为所有元素赋值,零初始化可能造成不必要的性能开销(虽然影响通常很小)。此时可以不初始化:int* arr = new int[1000];(元素值为未定义,需后续手动赋值)。

4.3 初始化与智能指针结合

使用智能指针(如std::unique_ptrstd::shared_ptr)管理动态内存时,同样支持初始化操作:

#include <iostream>
#include <memory> // 智能指针头文件

int main() {
    // unique_ptr初始化单个int
    std::unique_ptr<int> uPtr1 = std::make_unique<int>(100);
    std::cout << *uPtr1 << std::endl; // 输出:100
    
    // unique_ptr初始化数组(C++14及以上支持make_unique数组版本)
    auto uPtr2 = std::make_unique<int[]>(3);
    uPtr2[0] = 1;
    uPtr2[1] = 2;
    uPtr2[2] = 3;
    
    // shared_ptr初始化自定义类型
    auto sPtr = std::make_shared<Point>(5, 6); // Point是前面定义的结构体
    std::cout << "(" << sPtr->x << ", " << sPtr->y << ")" << std::endl; // 输出:(5, 6)
    
    return 0;
}
  • 注意:std::make_uniquestd::make_shared会自动进行初始化,是更安全的动态内存分配方式(避免内存泄漏和异常安全问题)。

4.4 初始化失败的情况

  • 若动态内存分配失败(如内存不足),new会抛出std::bad_alloc异常(默认行为),此时初始化不会执行。
  • 可以使用new (std::nothrow)避免抛出异常,此时分配失败返回nullptr,需手动检查:
#include <iostream>
#include <new> // 包含nothrow

int main() {
    // 不抛出异常的分配方式
    int* ptr = new (std::nothrow) int[1000000000000]; // 尝试分配过大内存
    if (ptr == nullptr) {
        std::cout << "内存分配失败!" << std::endl;
    } else {
        // 分配成功,进行初始化
        *ptr = 10;
        delete[] ptr;
    }
    return 0;
}

5. 动态内存初始化的实际应用示例

示例1:动态字符串初始化

#include <iostream>
#include <string>

int main() {
    // 初始化动态字符串
    std::string* str1 = new std::string("Hello, 动态内存!");
    std::cout << *str1 << std::endl; // 输出:Hello, 动态内存!
    
    // 初始化空字符串(调用默认构造函数)
    std::string* str2 = new std::string();
    *str2 = "通过赋值修改内容";
    std::cout << *str2 << std::endl; // 输出:通过赋值修改内容
    
    delete str1;
    delete str2;
    
    return 0;
}

示例2:动态二维数组初始化

#include <iostream>

int main() {
    int rows = 3;
    int cols = 4;
    
    // 动态分配二维数组(数组的数组)
    int** matrix = new int*[rows];
    for (int i = 0; i < rows; i++) {
        // 初始化每行的元素(零初始化)
        matrix[i] = new int[cols]{};
        // 为每行赋值
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = i * cols + j;
        }
    }
    
    // 打印二维数组
    std::cout << "二维数组: " << std::endl;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            std::cout << matrix[i][j] << "\t";
        }
        std::cout << std::endl;
    }
    
    // 释放二维数组内存(先释放每行,再释放行指针数组)
    for (int i = 0; i < rows; i++) {
        delete[] matrix[i];
    }
    delete[] matrix;
    
    return 0;
}

输出结果:

二维数组: 
0	1	2	3	
4	5	6	7	
8	9	10	11	

总结

动态内存分配初始化是C++中确保内存安全使用的重要环节,本教程主要涵盖:

  1. 单个对象初始化:支持值初始化(())、列表初始化({})和零初始化,适用于基础类型和自定义类型(通过构造函数)。
  2. 动态数组初始化:C++11及以上支持列表初始化,未指定的元素会被零初始化;自定义类型数组需匹配相应的构造函数。
  3. 注意事项:优先使用花括号初始化提高兼容性,结合智能指针增强安全性,避免初始化方式导致的歧义或错误。
  4. 实际应用:动态字符串、二维数组等场景的初始化示例。

掌握动态内存初始化的各种方式,能帮助你写出更安全、更简洁的C++代码,减少因未初始化内存导致的程序错误。在实际开发中,建议结合智能指针(如std::make_unique)进行动态内存管理,进一步提升代码的可靠性。

1 条评论

  • @ 2025-8-10 15:08:35

    使用new操作符进行动态内存分配和初始化的示例代码 cpp 运行

    #include <iostream>
    #include <string>
    
    // 自定义结构体示例
    struct Person {
        std::string name;
        int age;
        
        // 构造函数
        Person(std::string n, int a) : name(n), age(a) {}
        // 默认构造函数
        Person() : name("未知"), age(0) {}
    };
    
    int main() {
        // 1. 基础类型的动态分配与初始化
        // 1.1 单个int初始化(值初始化)
        int* intPtr = new int(100);
        std::cout << "int值: " << *intPtr << std::endl;  // 输出:100
        
        // 1.2 单个double初始化(列表初始化,C++11+)
        double* doublePtr = new double{3.14159};
        std::cout << "double值: " << *doublePtr << std::endl;  // 输出:3.14159
        
        // 1.3 零初始化(不指定值,自动为0)
        bool* boolPtr = new bool();
        std::cout << "bool零值: " << std::boolalpha << *boolPtr << std::endl;  // 输出:false
        
        // 2. 自定义类型的动态分配与初始化
        // 2.1 调用带参构造函数
        Person* p1 = new Person("张三", 25);
        std::cout << "Person1: " << p1->name << ", " << p1->age << std::endl;  // 输出:张三, 25
        
        // 2.2 调用默认构造函数
        Person* p2 = new Person();
        std::cout << "Person2: " << p2->name << ", " << p2->age << std::endl;  // 输出:未知, 0
        
        // 3. 动态数组的分配与初始化
        // 3.1 基础类型数组(列表初始化,C++11+)
        int* intArr = new int[3]{1, 2, 3};
        std::cout << "int数组: ";
        for (int i = 0; i < 3; ++i) {
            std::cout << intArr[i] << " ";  // 输出:1 2 3
        }
        std::cout << std::endl;
        
        // 3.2 自定义类型数组(调用构造函数)
        Person* personArr = new Person[2]{Person("李四", 30), Person("王五", 28)};
        std::cout << "Person数组: " << std::endl;
        for (int i = 0; i < 2; ++i) {
            std::cout << personArr[i].name << ", " << personArr[i].age << std::endl;
            // 输出:李四, 30  王五, 28
        }
        
        // 释放动态内存
        delete intPtr;
        delete doublePtr;
        delete boolPtr;
        delete p1;
        delete p2;
        delete[] intArr;
        delete[] personArr;
        
        return 0;
    }
    
    • 1