• C++
  • C++ 结构体初始化和结构体的动态内存分配初始化学习笔记教程

  • @ 2025-8-10 14:31:21

C++ 结构体初始化与动态内存分配学习笔记

结构体是C++中一种重要的数据结构,允许将不同类型的数据组合在一起形成一个新的类型。本文将详细介绍结构体的初始化方法以及动态内存分配相关知识。

一、结构体的定义

首先,我们需要了解如何定义一个结构体:

// 定义一个学生结构体
struct Student {
    std::string name;  // 姓名
    int age;           // 年龄
    float score;       // 分数
};

二、结构体的初始化方法

1. 声明时初始化

最直接的初始化方式是在声明结构体变量时进行初始化:

// C++11之前的初始化方式
Student s1 = {"张三", 18, 90.5f};

// C++11及以后支持的列表初始化(推荐)
Student s2{"李四", 19, 88.0f};

2. 使用构造函数初始化

结构体可以像类一样拥有构造函数,这是更灵活的初始化方式:

struct Student {
    std::string name;
    int age;
    float score;
    
    // 构造函数
    Student(std::string n, int a, float s) {
        name = n;
        age = a;
        score = s;
    }
    
    // 带默认参数的构造函数
    Student(std::string n = "未知", int a = 0, float s = 0.0f) {
        name = n;
        age = a;
        score = s;
    }
};

// 使用构造函数初始化
Student s3("王五", 20, 95.5f);
Student s4;  // 使用默认参数初始化

3. 成员逐个初始化

可以先声明结构体变量,再逐个初始化其成员:

Student s5;
s5.name = "赵六";
s5.age = 19;
s5.score = 89.0f;

三、结构体的动态内存分配

当需要在程序运行时创建结构体实例时,我们可以使用new运算符进行动态内存分配。

1. 动态分配单个结构体

// 动态分配一个Student结构体
Student* pStudent1 = new Student;

// 初始化动态分配的结构体
pStudent1->name = "钱七";
pStudent1->age = 20;
pStudent1->score = 92.5f;

// 使用构造函数初始化
Student* pStudent2 = new Student("孙八", 19, 87.0f);

2. 动态分配结构体数组

// 动态分配结构体数组
Student* pStudents = new Student[3];

// 初始化数组元素
pStudents[0] = Student("周九", 18, 91.0f);
pStudents[1] = Student("吴十", 19, 85.5f);
pStudents[2] = Student("郑十一", 20, 88.0f);

3. 释放动态分配的内存

动态分配的内存需要手动释放,否则会导致内存泄漏:

// 释放单个结构体
delete pStudent1;
delete pStudent2;

// 释放结构体数组(注意使用delete[])
delete[] pStudents;

// 释放后将指针置空,避免野指针
pStudent1 = nullptr;
pStudent2 = nullptr;
pStudents = nullptr;

四、完整示例代码

下面是一个包含上述所有知识点的完整示例:

#include <iostream>
#include <string>

// 定义带构造函数的结构体
struct Student {
    std::string name;
    int age;
    float score;
    
    // 默认构造函数
    Student() : name("未知"), age(0), score(0.0f) {}
    
    // 带参数的构造函数
    Student(std::string n, int a, float s) 
        : name(n), age(a), score(s) {}
        
    // 显示学生信息的成员函数
    void display() {
        std::cout << "姓名: " << name 
                  << ", 年龄: " << age 
                  << ", 分数: " << score << std::endl;
    }
};

int main() {
    // 1. 基本初始化方式
    Student s1 = {"张三", 18, 90.5f};  // 传统方式
    Student s2{"李四", 19, 88.0f};     // C++11列表初始化
    
    // 2. 使用构造函数初始化
    Student s3("王五", 20, 95.5f);
    Student s4;  // 使用默认构造函数
    
    // 3. 成员逐个初始化
    Student s5;
    s5.name = "赵六";
    s5.age = 19;
    s5.score = 89.0f;
    
    // 4. 动态分配单个结构体
    Student* pStudent1 = new Student;
    pStudent1->name = "钱七";
    pStudent1->age = 20;
    pStudent1->score = 92.5f;
    
    Student* pStudent2 = new Student("孙八", 19, 87.0f);
    
    // 5. 动态分配结构体数组
    Student* pStudents = new Student[3];
    pStudents[0] = Student("周九", 18, 91.0f);
    pStudents[1] = Student("吴十", 19, 85.5f);
    pStudents[2] = Student("郑十一", 20, 88.0f);
    
    // 显示所有学生信息
    std::cout << "学生信息列表:" << std::endl;
    s1.display();
    s2.display();
    s3.display();
    s4.display();
    s5.display();
    pStudent1->display();
    pStudent2->display();
    
    for (int i = 0; i < 3; i++) {
        pStudents[i].display();
    }
    
    // 释放动态分配的内存
    delete pStudent1;
    delete pStudent2;
    delete[] pStudents;
    
    // 避免野指针
    pStudent1 = nullptr;
    pStudent2 = nullptr;
    pStudents = nullptr;
    
    return 0;
}

五、注意事项

  1. 内存管理:使用new分配的内存必须用delete释放,数组则用delete[],否则会导致内存泄漏。

  2. 构造函数:如果定义了带参数的构造函数,C++不会自动生成默认构造函数,需要手动定义。

  3. 初始化列表:在构造函数中使用初始化列表(如Student(std::string n) : name(n) {})比在构造函数体内赋值更高效。

  4. 空指针:释放内存后,应将指针设置为nullptr,避免成为野指针。

  5. C++11特性:列表初始化是C++11引入的特性,使初始化更加简洁直观,推荐使用。

通过以上学习,你应该已经掌握了C++结构体的各种初始化方法和动态内存分配技巧。在实际编程中,根据具体场景选择合适的初始化方式,可以使代码更加高效和易读。

2 条评论

  • @ 2025-8-10 14:41:06

    C++结构体使用new{}初始化教程

    在C++11及以后的标准中,引入了列表初始化(List Initialization)语法,使用大括号{}进行初始化,这为结构体的动态内存分配提供了更灵活、更安全的方式。本教程将详细介绍如何使用new{}语法初始化结构体。

    一、new{}初始化的基本语法

    使用new{}初始化结构体的基本语法如下:

    // 动态分配并初始化单个结构体
    结构体类型* 指针变量 = new 结构体类型{初始化值列表};
    
    // 动态分配并初始化结构体数组
    结构体类型* 指针变量 = new 结构体类型[数组大小]{初始化值列表1, 初始化值列表2, ...};
    

    二、使用new{}初始化不同类型的结构体

    1. 聚合结构体(无自定义构造函数)

    对于没有定义任何构造函数的结构体(聚合类型),new{}可以直接初始化其成员:

    #include <iostream>
    #include <string>
    
    // 1. 聚合结构体(无自定义构造函数)
    struct Point {
        int x;
        int y;
    };
    
    struct Person {
        std::string name;
        int age;
        double height;
    };
    
    // 2. 带构造函数的结构体
    struct Student {
        std::string name;
        int id;
        float score;
        
        // 带参数的构造函数
        Student(std::string n, int i, float s) 
            : name(n), id(i), score(s) {}
            
        // 显示信息
        void display() const {
            std::cout << "姓名: " << name 
                      << ", 学号: " << id 
                      << ", 成绩: " << score << std::endl;
        }
    };
    
    // 3. 嵌套结构体
    struct Rectangle {
        Point topLeft;
        Point bottomRight;
        std::string color;
    };
    
    int main() {
        // 示例1: 初始化聚合结构体
        std::cout << "=== 初始化聚合结构体 ===" << std::endl;
        
        // 初始化Point结构体
        Point* p1 = new Point{10, 20};
        std::cout << "Point p1: (" << p1->x << ", " << p1->y << ")" << std::endl;
        
        // 初始化Person结构体
        Person* person1 = new Person{"张三", 25, 1.75};
        std::cout << "Person: " << person1->name << ", " 
                  << person1->age << "岁, " 
                  << person1->height << "米" << std::endl;
        
        // C++20支持指定成员初始化
        Person* person2 = new Person{.name="李四", .age=30};
        std::cout << "Person: " << person2->name << ", " 
                  << person2->age << "岁, " 
                  << person2->height << "米" << std::endl;
        
        // 示例2: 初始化带构造函数的结构体
        std::cout << "\n=== 初始化带构造函数的结构体 ===" << std::endl;
        
        // new{}会调用匹配的构造函数
        Student* stu1 = new Student{"王五", 1001, 92.5f};
        stu1->display();
        
        // 示例3: 初始化嵌套结构体
        std::cout << "\n=== 初始化嵌套结构体 ===" << std::endl;
        
        Rectangle* rect = new Rectangle{
            {0, 0},       // topLeft
            {100, 200},   // bottomRight
            "blue"        // color
        };
        std::cout << "矩形: 左上角(" << rect->topLeft.x << "," << rect->topLeft.y 
                  << "), 右下角(" << rect->bottomRight.x << "," << rect->bottomRight.y 
                  << "), 颜色:" << rect->color << std::endl;
        
        // 示例4: 初始化结构体数组
        std::cout << "\n=== 初始化结构体数组 ===" << std::endl;
        
        // 初始化Point数组
        Point* points = new Point[3]{
            {0, 0},
            {10, 10},
            {20, 20}
        };
        std::cout << "Points数组:" << std::endl;
        for (int i = 0; i < 3; i++) {
            std::cout << "(" << points[i].x << ", " << points[i].y << ")" << std::endl;
        }
        
        // 初始化Student数组
        Student* students = new Student[2]{
            {"赵六", 1002, 88.0f},
            {"孙七", 1003, 95.5f}
        };
        std::cout << "Students数组:" << std::endl;
        for (int i = 0; i < 2; i++) {
            students[i].display();
        }
        
        // 释放内存
        delete p1;
        delete person1;
        delete person2;
        delete stu1;
        delete rect;
        delete[] points;
        delete[] students;
        
        // 避免野指针
        p1 = nullptr;
        person1 = person2 = nullptr;
        stu1 = nullptr;
        rect = nullptr;
        points = nullptr;
        students = nullptr;
        
        return 0;
    }
        
    

    2. 带构造函数的结构体

    当结构体有自定义构造函数时,new{}会根据初始化列表调用相应的构造函数:

    // 结构体定义
    struct Student {
        std::string name;
        int id;
        float score;
        
        // 带参数的构造函数
        Student(std::string n, int i, float s) 
            : name(n), id(i), score(s) {}
    };
    
    // 使用new{}初始化
    Student* stu = new Student{"王五", 1001, 92.5f};
    

    3. 嵌套结构体

    对于包含其他结构体的嵌套结构体,new{}可以通过嵌套的大括号进行初始化:

    // 嵌套结构体初始化
    Rectangle* rect = new Rectangle{
        {0, 0},       // 内部结构体topLeft的初始化
        {100, 200},   // 内部结构体bottomRight的初始化
        "blue"        // color成员
    };
    

    4. 结构体数组

    使用new[]配合{}可以方便地初始化结构体数组:

    // 初始化结构体数组
    Point* points = new Point[3]{
        {0, 0},
        {10, 10},
        {20, 20}
    };
    

    三、new{}初始化的优势

    1. 一致性:统一的初始化语法,适用于各种数据类型
    2. 安全性:不允许窄化转换(如将double隐式转换为int)
    3. 灵活性:可以初始化聚合类型、带构造函数的类型和嵌套类型
    4. 明确性:初始化值与成员的对应关系清晰
    5. C++20增强:支持指定成员初始化(如.name="李四"

    四、注意事项

    1. C++版本new{}是C++11引入的特性,需要编译器支持C++11及以上标准
    2. 窄化转换new{}不允许窄化转换,例如new Point{10.5, 20}会编译错误
    3. 聚合类型:如果结构体有用户定义的构造函数,就不再是聚合类型,不能使用聚合初始化
    4. 内存释放:使用new分配的内存必须用delete释放,数组用delete[]
    5. 指针安全:释放内存后,应将指针置为nullptr,避免野指针

    五、总结

    new{}初始化语法为C++结构体的动态内存分配提供了一种现代、安全且灵活的方式。无论是简单的聚合结构体,还是复杂的带构造函数的结构体,甚至是结构体数组和嵌套结构体,new{}都能提供一致且清晰的初始化体验。在实际开发中,推荐优先使用new{}语法进行结构体的动态初始化。

    • @ 2025-8-10 14:37:32

      C++结构体使用new()和new{}初始化教程

      在C++中,使用new运算符动态分配结构体时,可以通过不同的语法进行初始化。本教程将详细介绍使用new()new{}初始化结构体的方法及区别。

      一、基础概念

      • new():使用圆括号进行初始化,支持构造函数参数传递
      • new{}:使用大括号进行列表初始化(C++11及以后),支持列表初始化语法

      这两种方式在初始化结构体时各有特点,适用于不同场景。

      二、使用new()初始化结构体

      new()语法主要用于调用结构体的构造函数进行初始化,语法格式为:

      结构体指针 = new 结构体名(构造函数参数);
      

      示例代码:

      #include <iostream>
      #include <string>
      
      // 定义一个学生结构体
      struct Student {
          std::string name;
          int age;
          float score;
          
          // 默认构造函数
          Student() : name("未知"), age(0), score(0.0f) {
              std::cout << "调用了默认构造函数" << std::endl;
          }
          
          // 带参数的构造函数
          Student(std::string n, int a, float s) 
              : name(n), age(a), score(s) {
              std::cout << "调用了带参数的构造函数" << std::endl;
          }
          
          // 显示信息
          void showInfo() const {
              std::cout << "姓名: " << name 
                        << ", 年龄: " << age 
                        << ", 分数: " << score << std::endl;
          }
      };
      
      int main() {
          // 1. 使用new()初始化
          std::cout << "=== 使用new()初始化 ===" << std::endl;
          
          // 1.1 调用默认构造函数
          Student* s1 = new Student();
          s1->showInfo();
          
          // 1.2 调用带参数的构造函数
          Student* s2 = new Student("张三", 18, 90.5f);
          s2->showInfo();
          
          // 2. 使用new{}初始化 (C++11及以后)
          std::cout << "\n=== 使用new{}初始化 ===" << std::endl;
          
          // 2.1 不带参数,调用默认构造函数
          Student* s3 = new Student{};
          s3->showInfo();
          
          // 2.2 使用列表初始化,相当于调用相应的构造函数
          Student* s4 = new Student{"李四", 19, 88.0f};
          s4->showInfo();
          
          // 2.3 部分成员初始化(需要结构体没有用户定义的构造函数)
          // 注意:如果结构体有用户定义的构造函数,这种方式会报错
          struct Point {
              int x;
              int y;
          };
          
          Point* p1 = new Point{10, 20};  // 正确,初始化x=10, y=20
          Point* p2 = new Point{x=30, y=40};  // C++20支持指定成员初始化
          std::cout << "\nPoint p1: (" << p1->x << ", " << p1->y << ")" << std::endl;
          std::cout << "Point p2: (" << p2->x << ", " << p2->y << ")" << std::endl;
          
          // 3. 动态分配结构体数组
          std::cout << "\n=== 动态分配结构体数组 ===" << std::endl;
          
          // 3.1 使用new[]()
          Student* class1 = new Student[2]{{"王五", 20, 92.0f}, {"赵六", 19, 89.5f}};
          // 3.2 使用new[]{}
          Student* class2 = new Student[2]{"钱七", 21, 95.0f, "孙八", 20, 87.5f};
          
          std::cout << "class1 学生信息:" << std::endl;
          for (int i = 0; i < 2; i++) {
              class1[i].showInfo();
          }
          
          std::cout << "class2 学生信息:" << std::endl;
          for (int i = 0; i < 2; i++) {
              class2[i].showInfo();
          }
          
          // 释放内存
          delete s1;
          delete s2;
          delete s3;
          delete s4;
          delete p1;
          delete p2;
          delete[] class1;
          delete[] class2;
          
          // 避免野指针
          s1 = s2 = s3 = s4 = nullptr;
          p1 = p2 = nullptr;
          class1 = class2 = nullptr;
          
          return 0;
      }
          
      

      三、new()与new{}的主要区别

      1. 初始化方式

        • new()主要通过调用构造函数进行初始化
        • new{}使用列表初始化,更灵活
      2. 对聚合类型的处理

        • 对于没有用户定义构造函数的结构体(聚合类型),new{}可以直接初始化成员
        • new()不能直接初始化聚合类型的成员
      3. 窄化转换

        • new{}不允许窄化转换(如int到float的隐式转换)
        • new()允许窄化转换
      4. 数组初始化

        • 两者都可以初始化数组,但new{}的语法更直观

      四、使用建议

      1. 当结构体有定义好的构造函数时,两种方式均可使用,但new{}的语法更统一
      2. 对于简单的聚合结构体(无自定义构造函数),优先使用new{}
      3. 在需要明确指定初始化成员时(C++20),使用new{成员名=值}的形式
      4. 初始化数组时,new{}的列表初始化语法更清晰

      通过合理选择new()new{}初始化方式,可以使代码更加清晰、安全和高效。在实际开发中,推荐优先使用new{}语法,因为它提供了更一致的初始化体验和更好的类型安全检查。

      • 1