C++ 内存区展开目录
C++ 内存通常分为 4 个区域:
- 全局数据区(data area);
- 代码区(code area);
- 堆区(自由存储区)(heap area);
- 栈区(stack area);
在 C 语言中,我们通过 malloc 或者 calloc 申请的空间即为堆区的空间,使用完成后用 free 归还申请的内存;而在 C++ 中我们用 new 申请堆区内存,delete 释放内存。操作堆内存时,有借有还,分配了堆内存就要记得对其进行回收,当然,这在 C++ 中是一件很麻烦的事情。
new 和 delete 展开目录
C++ 是面向对象编程语言,类和对象变得尤为重要,也是 C++ 与 C 语言的主要区分标志,在 C++ 中我们不能再依赖 C 语言中的 malloc () 等函数申请内存,其中一个原因是,它不能在分配空间时调用类中的构造函数,然而类对象的建立正是由构造函数来完成的。
而 free () 函数也不会调用类中的析构函数,关于构造函数及析构函数详见文章。
通过 malloc 申请到的对象空间无非就是一个含有随机数据的类对象空间而已,值不确定,毫无意义,要使得对象有意义我们还需要在后续自行调用构造函数,十分不便,故 C++ 用 new 代替 malloc () 那是必然的。
使用 new 分配堆对象展开目录
C++ 的 new 和 deleta 机制简单易懂,以下程序片段演示了堆对象空间的申请
- class student //student类
- {
- public:
- //..
- private:
- //..
- };
- void func(){
- student* p;
- p=new student;//申请堆对象,p指向该对象地址,此时C++自动调用构造函数student()
- //..
- delete p;//释放堆对象的空间,此时C++自动调用析构函数~student()
- }
如果需要调用有参构造函数,参考以下程序片段
- class Tdate{
- public:
- Tdate(int m,int d,int y);//有参构造函数
- Tdate(){};//无参构造函数
- protected:
- int month;
- int day;
- int year;
- };
- Tdate::Tdate(int m,int d,int y)
- {
- //..
- }
- void fun(){
- Tdate* p;
- //p=new Tdate();调用无参构造函数的方法
- p=new Tdate(1,1,2021);//调用有参构造函数
- //..
- delete p;
- }
delete 与 delete [] 的使用展开目录
delete 的用法如以上内容,在其后跟上指向需要释放的空间的指针即可;而 delete [] 我们通过后面的 “[]” 就知道这是用于释放数组空间的,如果我们申请的是如下的堆空间
- void fun(){
- Tdate* p;
- p=new Tdate[5];//分配5个对象数组空间,此时只能调用默认的无参构造函数
- //..
- delete[] p;//释放这5个对象数组空间
- }
delete [] 即是告诉 C++ 该指针指向的是一个数组,[] 不需要写上数组长度,如果有,C++ 编译器也会将其忽略,但绝不能忘记写 []。
拷贝构造函数展开目录
拷贝构造函数,顾名思义,用于拷贝一个对象然后去构造另一个对象的函数,即用一个对象的值去初始化一个新构造的对象,如以下代码片段
- student s1("Henry");
- student s2=s1;//拷贝对象s1至s2,此时会调用拷贝构造函数
将对象作为函数参数传递时,也涉及对象的拷贝,因为函数调用涉及实参到形参的传递,也就是将实参对象拷贝到形参对象,对象的类型多种多样,很多对象中的数据并不像基本的 int、double 等能够简单的赋值拷贝,比如对象里可能存在用 new 申请的堆空间?那么显然就不能进行简单的赋值拷贝,这也就引出了拷贝构造函数的意义。
以下程序片段演示了如何编写我们需要的拷贝构造函数
- class student{
- private:
- string name;
- int id;
- public:
- student(string nName="null",int nId=0)//构造函数
- {
- this->name=nName;
- this->id=nId;
- cout<<"Constructing new student "<<nName<<endl;
- }
- student(student& s){//拷贝构造函数,student&为student类对象的引用,引用的内容参考前一篇文章
- cout<<"Constructing copy of "<<s.name<<endl;
- this->name="copy of "+s.name;
- this->id=s.id;
- }
- ~student(){
- cout<<"delete "<<this->name<<endl;
- }
- };
- void fun(student s){
- cout<<"In function fun()"<<endl;
- }
- int main()
- {
- student henry("Henry",21);
- cout<<"Calling fun()"<<endl;
- fun(henry);
- cout<<"Returned from fun()"<<endl;
- return 0;
- }
运行结果
- Constructing new student Henry
- Calling fun()
- Constructing copy of Henry
- In function fun()
- delete copy of Henry
- Returned from fun()
- delete Henry
默认拷贝构造函数展开目录
与构造函数类似,当开发者没有定义自己的拷贝构造函数时,C++ 将提供一个默认拷贝构造函数。
其工作方法为:完成一个成员一个成员的简单拷贝。
浅拷贝与深拷贝展开目录
浅拷贝即是像默认拷贝构造函数那样对数据成员进行简单的复制,那么如果对象中存在分配的资源(如堆内存)我们就不能在进行简单的浅拷贝,那样会使多个对象拥有同一块内存资源,如果其中一个对象遭到释放,那么其他对象将面临严重的内存堆栈错误,并且,在对象进行析构时,也会多次释放同一块资源,程序崩溃。
以下程序片段演示了深拷贝
- class student{
- public:
- student(char* nName);
- student(student& s);
- ~student();
- protected:
- char* name;
- };
- student::student(cahr* nName){//构造函数
- this->name=new char[strlen(nName)+1];//申请新内存
- if(nName)
- strcpy(this->name,nName);
- }
- student::student(student& s){//深拷贝构造函数
- this->name=new char[strlen(s.name)+1];//申请新内存
- if(s.name)
- strcpy(this->name,s.name);
- }
- student::~student(){
- name[0]="\0";
- delete name;
- }
无名对象展开目录
仅简述无名对象的三种用法
- //此程序片段中student为一个类
- student& refs=student("Henry");//初始化引用
- student s=student("Henry");//初始化对象
- fun(student("Henry"));//作为函数参数
【补充】this 指针展开目录
this 指针,其实我们看名字可以知道,这个指针肯定是指向与自己相关的,或正在处理的内存空间。
的确如此,一个类中所有对象调用的成员函数都处于同一个代码段,成员函数为了区分数据成员属于哪一个对象,故出现了 this 指针。
如在对象 s 调用成员函数 set () 时:s.set (1,1,2021),成员函数 set 除了接受了 3 个实参外,还接受了一个对象的地址(对象 s 的地址),这个地址被隐含的形参 this 指针所获取即 this 相当于 & s,所有都数据成员的访问都隐含地被加上了 this->,在本文前面的代码片段中,我特意加上了 this->,方便读者理解。
- //以下三种数据成员访问方法等价
- month=m;
- this->month=m;
- s.month=m;
我们在一个成员函数需要返回当前处理的对象或对象的地址时,this 指针就成为了必要,如以下程序片段
- student student::fun(){
- //..
- return *this;//返回当前对象
- //return this;//返回指向当前对象的指针
- }
编辑:Henry 2021-03-04 未授权禁止转载
版权属于:字节星球 / 肥柴之家 (转载请联系作者授权)
原文链接:https://www.bytecho.net/archives/1710.html
本作品采用知识共享署名 - 非商业性使用 - 相同方式共享 4.0 国际许可协议进行许可。
【浅拷贝与深拷贝】里,为什么说:那么如果对象中存在分配的资源(如堆内存)我们就不能在进行简单的浅拷贝,那样会使多个对象拥有同一块内存资源。不太理解为什么会拥有同一块内存。
你好,因为浅拷贝对于字符数组等仅会复制其内存地址,也就使得你复制出来的新变量依然指向你复制的那个变量的地址。这也是为什么字符数组需要用 strcpy 复制的原因。其他需要深拷贝的案例也可以这样理解。
我知道了,谢谢博主 OωO
欸,那是不是类的成员变量里没有指针类型的变量,使用浅拷贝就不会发生指向同一块内存的问题了?
其实,只要理解 “拷贝” 究竟拷贝的是什么就可以了。
@(OK)