C++面向对象——构造函数与析构函数、内存分区模型
目录
构造函数
构造函数的三种方法
1.括号法(常用)
????:
student stu01(”张三”,190110101);
student stu02(stu01);
2.显式法
????:
student stu01 = student(”张三”,190110101);
student stu02 = student(stu01);
//匿名对象
student(”张三”,190110101);
特点:当前行执行结束后,系统立即回收匿名对象
不要利用拷贝构造函数初始化匿名对象
编译器会认为 student(stu02) === student stu02; 这是对象声明
3.隐式转换法
????:
student stu01 = 100;
student stu02 = stu01;
初始化列表
1.尽量使用初始化列表来初始化类
2.理由:使用构造函数时是先赋值再初始化,使用初始化列表可以直接完成初始化
3.类A做类B的成员变量时,应用初始化变量初始化类A,而非构造函数
4.不可对静态成员变量初始化列表,只能在类外定义时初始化
拷贝构造函数
1.拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。
2.拷贝构造函数通常用于:
(1)通过使用另一个同类型的对象来初始化新创建的对象。
(2)复制对象把它作为参数传递给函数。
(3)复制对象,并从函数返回这个对象。
3.拷贝构造函数的形式:
classname(const classname &obj)
{
//statement
}
注意事项
(1)不改变本体,加const
(2)对象前加引用符号&
????:
Line (int len);
Line (const Line &obj);
obj是一个对象引用,该对象是用于初始化另一个对象。
4.构造实例:注意类中带有指针变量,并有动态内存分配时的做法。(截自菜鸟教程——C++拷贝构造函数)
5.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用拷贝构造函数。当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式调用拷贝构造函数。
6.什么情况使用拷贝构造函数:
类的对象需要拷贝时,拷贝构造函数将会被调用。
浅拷贝与深拷贝
1.简单的赋值拷贝使用浅拷贝
2.成员变量含指针时使用深拷贝
定义类时使用:
private:
string *name;
此时使用深拷贝得到的name指向地址不同
拷贝构造函数中使用:
m_Name = new string (*name);
析构函数中使用:
if ( m_Name != NULL ){
delete m_Name;
m_Name = NULL;
}
使用if判断语句而不是直接delete或不是使用默认析构的原因是防止堆区内存重复释放
类对象作为其他类对象的成员变量
1.类中如果需要其他对象作为私有变量或者protected时,只能调用没有参数的构造函数。
2.若想在类中调用其它类的对象且带有参数的构造函数时,只能将其放入成员函数里对其进行调用。
诸多注意事项
1.构造函数的重载规则与普通函数的重载规则相同。
2.初始化参数列表的方法:
Student :: Student (string name,string desc)
: m_Name(name) , m_desc(desc)
注意:形参名与下方括号内名相同
3.拷贝构造函数调用时机:
(1)使用一个已经创建完毕的对象来初始化一个新对象
(2)值传递的方式给函数参数传值,即类对象做形参
(3)以值方式返回局部对象
????:
student test01()
{
student stu01;
return stu01;
}
void test02()
{
student stu02 = test01();
}
4.为避免初始化列表时出现错误,建议如下:
(1)总是按照你希望它们被初始化的顺序声明成员。
(2)总是按照它们声明的顺序罗列这些成员。
6.调用默认构造函数时不要加()
????:student stu01(); //错误!编译器认为这是一个返回student的无参函数
7.写了有参构造函数后编译器不再提供默认构造函数,此时若不写默认构造函数,书写以下代码会报错:
student stu01;
8.写了拷贝构造后编译器不再提供默认构造,也无带参构造
1.栈内存存放基本类型的变量,栈内数据存储速度快,但存储量不大。
2.堆内存存放动态申请的数据,全局变量,*度高,灵活。
析构函数
内存分区模型
1.C++程序执行将内存大致分为四区域
(1)代码区:存储函数体的二进制代码,由操作系统进行管理。
(2)全局区:存放全局变量,静态变量和常量。
(3)栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
(4)堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
2.程序运行前:
程序编译后,生成了exe可执行程序,未执行程序前分为两个区域
(1)代码区:
存放CPU执行的机器指令,分为共享与只读
共享的目的是对于频繁被执行的程序,只需在内存中有一份代码即可
只读的目的是防止程序意外地修改其指令
(2)全局区:
全局变量和静态变量存放于此
全局区还包含了常量区,字符串常量和其他常量(如const常量)也存放于此
该区域的数据在程序结束后由操作系统释放
3.程序运行后
(1)栈区
不要返回局部变量的地址,栈区的数据在函数执行完成后自动释放
形参数据也会放在栈区
(2)堆区
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
在C++中主要利用new在堆区开辟内存
????:
int *p = new int(10);
int *arr = new int[10];
利用new开辟一块新内存
利用指针p指向改内存
指针变量p本身在栈区
不要忘记在后面加上:
delete p;
delete[] arr; //注意释放数组内存的方法
此时再访问p所指内存即是非法操作
析构函数
1.对象过期时自动调用的特殊成员函数。
2.析构函数一般用来完成清理工作。
3.析构函数的名称是在类名前加~
4.析构函数没有参数,且只能有一个析构函数。
5.析构函数用来释放对象使用的资源,并销毁对象的非static数据成员。
6.无论何时一个对象被销毁,都会自动调用其析构函数(隐式析构)
7.(笔记写了很久自己也忘记这个截自哪里了...)
8.在栈内存申请的空间在使用后会被自动释放,而在堆内存申请的空间(动态内存分配)则必须手动释放。
????:
Student* stu5 = new Student(”Jack”,”Jacky”);
// processing
delete stu5;
9. 实战时多使用堆内存,少使用栈内存。
下一篇: 03.js输出数据的几种方式