跳转至

📔 第 12 章 类和动态内存分配 学习笔记

👉【复习题】【编程练习题

1. 动态内存和类

C++中使用 newdelete 运算符来动态控制内存,析构函数不可缺少,否则会导致很多新的编程问题的出现。有时必须重载赋值运算符,从而保证程序正常运行。

1.1 静态类成员

静态类成员的特点:无论创建多少个对象,程序都只创建一个静态类变量副本(类的所有对象共享同一个静态成员)。

不能在类声明中初始化静态成员变量(声明描述了如何分配内存,但不分配内存)。可以在类声明之外单独使用单独语句来进行初始化(静态成员变量是单独存储,而不属于对象的组成部分)。

⚠️注意:静态数据成员在类声明中声明,在包含类方法的文件中的初始化。初始化时使用作用域运算符来指出静态成员所属的类。但如果静态成员整型枚举型const,则可以在类声明中初始化

⚠️警告:在构造函数中使用new来分配内存时,必须在相应的析构函数中使用delete来释放内存。如果使用 new[] (包括中括号)分配内存,则应使用delete[](包括中括号)释放内存

自动存储对象被删除的顺序与创建顺序相反。

静态成员函数的声明中必须包含关键字 static,但如果函数定义是独立的,则其中不能包含关键字static。原因有二:

  • 不能通过对象调用静态成员函数,甚至不能使用this指针。
  • 静态成员函数不与特定的对象相关联,只能使用静态数据成员。

1.2 特殊成员函数

C++提供的隐式成员函数:

  • 默认构造函数,如果没有定义构造函数

    如果没有提供任何构造函数,C++将创建默认构造函数。

    StringBad::StringBad() {} // 隐式默认构造函数
    
    编译器将提供一个不接受任何参数、也不执行如何操作的构造函数(默认的默认构造函数)。

  • 默认析构函数,如果没有定义

  • 复制构造函数,如果没有定义

    • 定义

      复制构造函数用于将一个对象复制到新创建的对象中。用于初始化过程中(包括按值传递),而不是常规的赋值过程。类的复制构造函数的原型如下:

      Class_name(const Class_name &); //接收一个指向对象的常量引用作为参数
      

    • 何时调用?

      每当程序生成对象副本时,编译器都会使用复制构造函数。常见的情况:

      • 当函数按值传递对象时
      • 当函数返回对象时
      • 当编译器生成临时对象时
    • 默认的复制构造函数的功能

      默认的复制构造函数逐个复制非静态成员(成员复制,也称为浅拷贝),复制的是成员的值

      ⚠️警告:如果类中包含了使用new初始化的指针成员,应当定义一个拷贝构造函数,以复制指向的数据,而不是指针。这种称为深拷贝。浅拷贝只拷贝指针值(拷贝指针信息)

  • 赋值运算符,如果没有定义

    • 定义和原型函数

      C++允许类对象赋值,通过自动为类重载复制运算符来实现。函数原型如下:

      Class_name & Class_name::operator=(const Class_name &);
      

    • 赋值运算符的作用

      解决默认赋值运算符不合适导致的问题。

    • 实现复制运算符(进行深拷贝)的注意点:

      • 目标对象可能引用以前分配的数据,所以函数应使用 delete[] 来释放数据。
      • 函数应当避免将对象赋值给自身:否则给对象重新赋值时,释放内存操作可能删除对象的内存。
      • 函数返回一个指向调用对象的引用。
  • 地址运算符,如果没有定义

当使用一个对象来初始化另一个对象时,编译器将自动生成复制构造函数(创建对象的一个副本)。例如:

StringBad(const StringBad &)

2. 在构造函数中使用new时的注意事项

使用new初始化对象的指针成员时必须小心。具体做法如下:

  • 如果在构造函数中使用new来初始化指针成员,则应在析构函数中使用delete
  • new 和 delete必须相互兼容。new对应于deletenew[] 对应于 delete[]
  • 如果有多个构造函数,则必须以相同的方式使用 new,要么带括号,要么都不带。
  • 定义一个拷贝构造函数,通过深拷贝将一个对象初始化为另一个对象。

3. 返回对象的问题

当成员函数或独立的函数返回对象时,有3种返回方式

  • 指向对象的引用

  • 指向对象的const引用

  • const对象

4. 使用指向对象的指针

使用对象指针时的注意事项

  • 使用常规表示法来声明指向对象的指针。
  • 将指针初始化为指向已有的对象。
  • 可使用 new 来初始化指针,创建一个新的对象。
  • 对类使用 new 将调用相应的类构造函数来初始化新创建的对象。
  • 可使用 -> 运算符通过指针访问类方法。
  • 可对对象指针应用解除引用运算符来获得对象。

2. 队列模拟

待更新