0901 C++的动态内存分配与回收
Part 1.梳理思维导图
一.C++的动态内存分配与回收
1.C++和C中的分配与回收
1.C语言中用的是malloc和free两个函数
2.C++可以使用这两个函数,或者使用new和delete两个关键字
2.用new来内存分配
a.单个分配
格式:数据类型 *指针名 = new 数据类型
示例:int *p = new int;
b.连续分配
格式:数据类型 *指针名 = new 数据类型[个数]
示例:int *p = new int[5];
3.用delete来内存回收
a.单个回收
格式:delete 指针名
示例:delete p;
b.连续回收
格式:delete []指针名
示例:delete []p;
4.malloc和free与new和delete的区别
1.new和delete属于关键字,而malloc和free属于函数
2.new创建堆区空间是用数据类型作为单位,而malloc是用字节作为单位
3.new申请空间是可以初始化的,而malloc不可以
4.new返回的指针的数据类型是申请空间用的数据类型,而malloc是返回万能指针
5.new和delete分单个和多个,malloc和free不分
6.new申请类对象空间时,会自动调用构造函数,而malloc不会
7.delete释放类对象空间时,会自动调用析构函数,而free不会
二.类中特殊的成员函数
1.种类
1.构造函数 2.析构函数 3.拷贝构造函数 4.拷贝赋值函数 5.移动构造函数
6.移动赋值函数 7.取地址符重载 8.常取地址符重载
原因:1.这些成员函数,不用手动定义,系统会给,手动定义了之后,系统会取消原来的默认函数
2.这些成员函数不需要手动调用,会自动调用
2.构造函数
a.功能
用类实例化一个对象时,会自动调用该函数,是类对象申请空间和初始化的函数
b.格式
类名(形参列表)
{函数体内容;
}
c.使用场景
1.在栈区时:当实例化一个类对象时,会调用构造函数
2.在堆区时:当用new给申请一个类对象空间时,会调用构造函数
d.无参构造函数与有参构造函数
1.无参构造函数:形参列表没有任何参数,在请定义一个类对象时使用
2.有参构造函数:形参列表有参数,在初始化一个类对象时使用
#include <iostream>using namespace std;class Stu
{
private:string name;int age;double score;
public:Stu (){cout << "无参构造函数" << endl;}Stu (string name = "--",int age = 0,double score = 0){Stu::name = name;Stu::age = age;Stu::score = score;cout << "有参构造函数" << endl;}void output();
};void Stu::output()
{cout << "姓名:" << name << endl;cout << "年龄:" << age << endl;cout << "分数:" << score << endl;
}int main()
{Stu s1;Stu s2("张三",18,99);s2.output();Stu *p = new Stu();Stu *p2 = new Stu("李四",28,88);p2->output();Stu *p3 = (Stu *)malloc(sizeof(Stu));return 0;
}
e.初始化列表
e1.定义
在初始化一个类对象时使用,只有构造函数有初始化列表
e2.格式
类名(形参1,形参2,形参3,...,形参n):成员变量1(形参1),成员变量2(形参2),成员变量3(形参3),...,成员变量n(形参n)
{函数体内容;
}
#include <iostream>using namespace std;class Stu
{
private:string name;int age;double score;
public:Stu (string name = "--",int age = 0,double score = 0):name(name),age(age),score(score){cout << "有参构造函数" << endl;}void output();
};void Stu::output()
{cout << "姓名:" << name << endl;cout << "年龄:" << age << endl;cout << "分数:" << score << endl;
}int main()
{Stu s1;Stu s2("张三",18,99);s2.output();Stu *p = new Stu();Stu *p2 = new Stu("李四",28,88);p2->output();Stu *p3 = (Stu *)malloc(sizeof(Stu));return 0;
}
f.类的嵌套
1.先调用被嵌套类的构造函数
2.再调用嵌套类的构造函数
#include <iostream>using namespace std;class Score
{
private:int Chinesescore;int Mathscore;int Englishscore;
public:Score(){cout << "Score::无参构造函数" << endl;}Score(int Chinesescore,int Mathscore,int Englishscore):Chinesescore(Chinesescore),Mathscore(Mathscore), Englishscore(Englishscore){cout << "Score::有参构造函数" << endl;}
};class Stu
{
private:string name;int id;Score Sco;
public:Stu(){cout << "Stu::无参构造函数" << endl;}Stu(string name,int id,int Chinesescore,int Mathscore,int Englishscore):name(name),id(id),Sco(Chinesescore, Mathscore, Englishscore){cout << "Stu::有参构造函数" << endl;}};int main()
{Stu s1("张三",1001,99,88,77);return 0;
}
3.析构函数
a.功能
当一个类对象结束后,需要调用析构函数来回收资源
b.格式
~类名()
{函数体内容;
}
c.使用场景
1.在栈区:类对象结束后,析构函数会回收资源
2.在堆区:使用delete释放类对象空间时,会调用析构函数
d.示例
#include <iostream>using namespace std;class Time
{
private:int hour;int min;int score;
public:Time(){cout << "Time::无参构造函数" << endl;}Time(int hour,int min,int score):hour(hour),min(min),score(score){cout << "Time::有参构造函数" << endl;}~Time(){cout << this << endl;cout << "Time::析构函数" << endl;}
};int main()
{Time tm1;Time tm2(16,03,20);cout << &tm1 << &tm2 << endl;Time tm3(tm2);Time tm4 = tm3;return 0;
}
4.拷贝构造函数
a.功能
用一个类对象给另一个类对象初始化时,会调用拷贝构造函数
b.格式
类名(const 类名 &other)
{函数体内容;
}
c.使用场景
1.用一个类对象给另一个类对象初始化时,会调用拷贝构造函数
2.当类对戏作为函数实参,会调用拷贝构造函数
3.函数的返回值是一个类对象的话,会调用拷贝构造函数
#include <iostream>using namespace std;class Time
{
private:int hour;int min;int score;
public:Time(){cout << "Time::无参构造函数" << endl;}Time(int hour,int min,int score):hour(hour),min(min),score(score){cout << "Time::有参构造函数" << endl;}~Time(){cout << this << endl;cout << "Time::析构函数" << endl;}Time(const Time &other):hour(other.hour),min(other.min),score(other.score){cout << "Time::拷贝构造函数" << endl;}
};int main()
{Time tm1;Time tm2(16,03,20);cout << &tm1 << &tm2 << endl;Time tm3(tm2);//类对象给另一个类对象初始化Time tm4 = tm3;return 0;
}
d.浅拷贝和深拷贝
d1.定义
1.浅拷贝:当使用系统默认的拷贝构造函数,就是浅拷贝
2.深拷贝:当使用程序员自己定义的特殊拷贝构造函数,就是深拷贝
d2.为什么需要深拷贝(为什么需要自己定义拷贝构造函数)
当类成员里面有一个指针类型的成员,在使用浅拷贝时,系统默认直接赋值指针地址到新的类对象,此时两个类对象的指针成员的存储地址指向同一片区域,当有一个类对象改变时,会连带另一个类对象改变
d3.示例
#include <iostream>using namespace std;class Time
{
private:int hour;int min;int score;int *day;
public:Time(){cout << "Time::无参构造函数" << endl;}Time(int hour,int min,int score,int day):hour(hour),min(min),score(score),day(new int(day)){cout << "Time::有参构造函数" << endl;}~Time(){delete day;day = nullptr;cout << "Time::析构函数" << endl;}
// Time(const Time &other):hour(other.hour),min(other.min),score(other.score),day(other.day)
// {
// cout << "Time::浅拷贝构造函数" << endl;
// }Time(const Time &other):hour(other.hour),min(other.min),score(other.score),day(new int(*(other.day))){cout << "Time::深拷贝构造函数" << endl;}
};int main()
{Time tm1;Time tm2(16,03,20,1);cout << &tm1 << &tm2 << endl;Time tm3(tm2);Time tm4 = tm3;return 0;
}
Part 2.习题
设计一个Per类,类中包含私有成员:姓名、年龄、指针成员身高、体重,再设计一个Stu类,类中包含私有成员:成绩、Per类对象p1,设计这两个类的构造函数、析构函数和拷贝构造函数。
#include <iostream>using namespace std;class Per
{
private:string name;int age;double *high;double weight;
public:Per(string name,int age,double high,double weight):name(name),age(age),high(new double(high)),weight(weight){cout << "Per::有参构造函数" << endl;}~Per(){delete high;high = nullptr;cout << "Per::析构函数" << endl;}Per(const Per &other):name(other.name),age(other.age),high(new double(*(other.high))),weight(other.weight){cout << "Per::拷贝构造函数" << endl;}void output(){cout << "姓名: " << name << ", 年龄: " << age << ", 身高: " << *high << ", 体重: " << weight << endl;}
};
class Stu
{
private:double score;Per p1;
public:Stu(double score,string name,int age,double high,double weight):score(score),p1(name,age,high,weight){cout << "Stu::有参构造函数" << endl;}~Stu(){cout << "Stu::析构函数" << endl;}Stu(const Stu &other):score(other.score),p1(other.p1){cout << "Stu::拷贝构造函数" << endl;}void output(){cout << "分数: " << score << endl;p1.output();}};int main()
{Stu s1(99,"张三",12,180,130);s1.output();Stu s2(s1);s2.output();return 0;
}