导图社区 第十二章类和动态内存分配
C 自学笔记 第十二章类和动态内存分配,整理了动态内存和类、改进后的新string类、在构造函数中使用new时应注意的事项、有关返回对象的说明、使用指向对象的指针、复习各种技术、队列模拟的知识,快来看看。
编辑于2023-04-13 09:13:17 广东第十二章 类和动态内存分配
1. 动态内存和类
1. 复习示例和静态类成员
2. 注意
1. 不能在类声明中初始化静态数据成员是一种例外情况,而静态成员为const类型或枚举类型必须为在类声明中初始化。
2. 在创建的类对象中,字符串所保存的位置不处于类对象的位置,而是单独保存在堆内存中,对象仅保存了指针
A. 删除对象可以释放对象本身占用的内存,而不能自动释放对象成员的指针指向的内存
B. 所以在析构函数中使用了delete语句来删除new分配的空间
3. 复制构造函数
1. StringBad(const Stringbad&)
2. 注意当使用一个类对象来为另外一个类对象赋值的时候,将自动生成类的复制构造函数,将创建关于类对象的一个副本,并会调用析构函数来删除临时的类对象
4. 特殊成员函数
C++将自动提供以下的成员函数 (如果没有定义)
1. 默认构造函数
2. 默认析构函数
3. 复制构造函数
1. Class_name(const Class_name&)
2. 使用条件
用于初始化过程中(包括按值传递参数)
3. 举例
1. 新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用
2. StringBad ditto(motto)
3. StringBad metto = motto
4. StringBad also = StringBad(motto)
将使用复制构造函数直接创建一个临时变量,然后将临时变量赋值给左侧类对象,
5. StringBad * pStringBad = new StringBad(motto)
将创建一个临时变量,并将临时变量赋值给一个匿名变量,后返回匿名对象的指针
注意这里创建的临时变量将会调用析构函数进行删除
4. 赋值构造函数默认的功能
复制成员的值
注意静态成员不受影响,他们不属于类的对象中
注意如果成员为指针,仅仅复制指向的地址,而没有新开辟内存
4. 赋值运算符
5. 地址运算符
即this指针
stringbad中的问题由隐式复制构造函数和隐式赋值运算符引起
5. 哪里出了问题?
1. 问题
复制构造函数没有 num_string++
复制构造函数新创建了两个对象,并调用了析构函数delete了临时对象,导致对象中指针指向的字符串的内存被释放
2. 解决
1. 定义一个显示复制构造函数以解决问题
即进行深度复制
即如果类中包含了使用new初始化的指针成员,则应当定义一个复制构造函数,以复制指向的数据而不是指针,这样子不会delete两次,或出现其他错误
6. StringBad的其他问题:赋值运算符
1. 原型
Class_name& Class_name::operator=(const Class_name&);
2. 功能及使用条件
A. StringBad headline1("Celery Stalks at Midnight")
B. StringBad knot
C. knot = headline1
这里会调用 复制构造函数来创建临时对象,然后通过赋值将临时对象赋值到新对象中,注意这里的默认赋值运算符也将为直接复制成员的值
即赋值运算符将使用临时变量的引用(但这里可能有问题?临时变量不应该被删除了吗),返回一个对应生成的类对象的引用
3. 赋值的问题出在哪里
因直接复制类成员,导致数据受损
4. 解决赋值的问题
1. 定义一个显示赋值运算符
条件
1. 由于目标可能引用了以前分配的数据,所以函数应该使用delete[ ]来释放这些数据
2. 函数应当避免将对象赋给自身,否则给对象重新赋值之前,释放内存操作可能删除对象中的内容
3. 函数返回一个指向调用对象的引用
2. 函数原型
3. S0 = S1 = S2
》》
S0.operator=(S1.operator=(S2);
返回的是类对象的引用
2. 改进后的新string类
1. 修订后的默认构造函数
C++中的空指针
1. (void *)0
2. NULL
3. nullptr
str = nullptr
2. 比较成员函数
标准的strcmp函数
1. 依照字母的顺序,第一个参数位于第二个参数之前,则函数返回一个负值
2. 若两个字符串相同,则返回0
3. 如果第一个参数位于第二个参数之后,则返回一个正值
注意
1. if (“love”== answer)
2. 被转化为 if(operator ==(“love”,answer))
3. 使用构造函数if(operator ==(String(“love”),answer)
3. 使用中括号表示法访问字符
1. 注意在表达式 city[0]中,city是第一个操作数,[ ]是运算符,0是第二个操作数
2.
注意 cout << opera[ 4 ]
将转化为 cout << opera.operator[ ](4)
这里可以进行赋值, means[0]=‘r’
4. 静态类成员函数
定义
成员函数可被声明为static(函数声明必须包含关键字static,但如果函数定义是独立的,就不能包含关键字static)
注意
1. 不能通过对象调用静态成员函数
1. 甚至不能使用this指针
2. 如果静态类成员函数是在公有部分声明的,则可以使用类名和作用域解析运算符来调用它
1. static int Howmany(){return num_strigns}
2. int count = String::Howmant();
2. 静态成员函数不能与特定的对象相关联,只能使用静态数据成员
而可以使用静态类成员函数控制某些标记
5. 进一步重载赋值运算符
解决的问题
之前学习的复制构造函数是通过只有一个参数的构造函数来创建临时变量,并将临时变量传递给需要赋值的对象,赋值运算符是从一个类的引用到另外一个类的引用
这里新重载的赋值运算符是从基本变量直接赋值给一个类(通过类调用)
6. 重载输入运算符
3. 在构造函数中使用new时应注意的事项
1. 注意事项
1. 如果在构造函数中使用new来初始化指针对象,则应该在析构函数中使用delete
2. new和delete必须互相兼容,new对应delete——new[ ]对应delete[ ]
3. 如果有多个构造函数,则必须以相同的方式来使用new,因为只有一个析构函数
4. 有关返回对象的说明
1. 返回指向const对象的引用
1. 返回对象将会调用构造函数,而返回引用不会
2. 引用指向的对象应该在调用函数执行时存在
2. 返回指向非const对象的引用
1. 常见于
重载赋值运算符
返回引用类型不会调用赋值构造函数
没有使用引用将会调用赋值构造函数来创建临时变量
<<运算符
必须使用引用,因为ostream类没有公有的复制构造函数
3. 返回对象
1. 若为局部变量,则返回对象,因返回引用将导致临时变量不存在
4. 返回const对象
1. 可以补捉无法发现的错误
5. 使用指向对象的指针
1. 定义
1. String* favorite = new String(Typename);
2. 在这里将调用构造函数或复制构造函数,这取决与Typename的类型
1. 为类——调用复制构造函数来创建一个匿名类对象
2. 为基本类型——调用构造函数来创建一个匿名类对象
2. 再谈new和delete
1. 如果类成员中有指针类型,在使用new运算符为类成员指针类型赋值新内存时
1. delete对象将释放类对象的内存
2. 而类对象的指针所指向的内存并不会被自动释放,需要使用析构函数来释放
3. 再谈定位new运算符
1. 注意事项
1. 如果使用new运算符创建一部分区域用来动态存储类对象
注意存储间隔位置
注意delete其中new出来的内容并不会调用相应的析构函数
2. 应该显示调用析构函数(注意按照创建顺序相反的方向删除)
pc3 -> ~JustTesting( );
pc1-> ~JustTesting( );
6. 复习各种技术
7. 队列模拟
1. 注意点
1. 对于const数据成员,必须在类对象创建之前对其初始化——成员列表初始化
Queue::Queue(int qs) { front = rear = NULL; items = 0 ; qsize = qs }
因qsize是const类型无法实现
Queue::Queue(int qs) : qsize(qs) { front = rear = NULL; items = 0 ; }
2. 成员列表初始化
A. 只能用于构造函数
B. 必须用这种格式来初始化非静态const数据成员
静态const成员可以使用 enum
C. 必须用这种格式来初始化引用数据成员