04_第14章 堆与拷贝构造函数

合集下载

(完整版)拷贝构造函数

(完整版)拷贝构造函数
}
};
//全局函数
CExample g_Fun()
{
CExample temp(0);
return temp;
}
int main()
{
g_Fun();
return 0;
}
当g_Fun()函数执行到return时,会产生以下几个重要步骤:
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);(3). 在函数执行到最后先析构temp局部变量。
class CExample {
private:
int a;
public:
//构造函数
CExample(int b)
{ a = b;}
//拷贝构造函数
CExample(const CExample& C)
{
a = C.a;
}
//一般函数
void Show ()
{
cout<<a<<endl;
}
};
int main()
a = C.a;
cout<<"copy"<<endl;
}
//析构函数
~CExample()
{
cout<< "delete: "<<a<<endl;
}
void Show ()
{
cout<<a<<endl;
}
};
//全局函数,传入的是对象
void g_Fun(CExample C)

拷贝构造函数与移动构造函数

拷贝构造函数与移动构造函数

拷贝构造函数与移动构造函数⼀、拷贝构造函数当类没有定义拷贝构造函数的时候,编译器会默认提供⼀个,这个拷贝函数是浅拷贝。

如果该类中含有指针,可能会发⽣内存泄漏,见下⾯的例⼦:class Test{public:int *p;Test(){ p=new int; };~Test(){ delete p; };};void main(){Test t1;Test t2(t1);Test t3 = t1;}t1、t2、t3的成员变量p指向的是同⼀块内存,程序结束后会出现重复释放的问题。

为了解决这个问题,可以⾃定义拷贝构造函数:class Test{public:int *p;Test(const Test &t){p = new int (*(t.p));}Test(){ p=new int; };~Test(){ delete p; };};⼆、右值引⽤除了上述的解决⽅法,还可以使⽤C++11的【右值引⽤】新特性来解决,⽽且可以提⾼程序的性能,减少内存开销。

为了引出左值引⽤的概念,先来复习左值和右值1.左值和右值int a = 3 + 4 ;上⾯的式⼦中,变量 a 就是左值,右边的表达式会⽣成⼀个临时变量存放 (3+4) 的值,这个变量称之为右值。

有两种⽅式可以判断:(1)只能放在等号(=)右侧的即为右值,可以放在左侧的为左值int a = 10 ;10 = a ; //错误(2)左值可以取地址,⽽右值不允许:int a = 3 + 4 ;int * b = & a ; //okb = & (3+4) ; //错误2.右值引⽤使⽤⽅法如下,b就是对右值 (3+4) 的引⽤。

int && b = 3 + 4 ;先看下下⾯的左值引⽤:int a = 0 ;int &b = 4 ; //错误!int &b = a ; //左值引⽤如上例所⽰,左值引⽤只能对左值进⾏别名引⽤,⽆法引⽤右值于是C++11增加了右值引⽤,使⽤ && 表⽰(和逻辑运算中的”且“⼀致)。

C++拷贝(复制)构造函数详解

C++拷贝(复制)构造函数详解

C++拷贝(复制)构造函数详解⼀. 什么是拷贝构造函数⾸先对于普通类型的对象来说,它们之间的复制是很简单的,例如:[c-sharp]1. int a = 100;2. int b = a;⽽类对象与普通对象不同,类对象内部结构⼀般较为复杂,存在各种成员变量。

下⾯看⼀个类对象拷贝的简单例⼦。

[c-sharp]1. #include <iostream>2. using namespace std;3.4. class CExample {5. private:6. int a;7. public:8. //构造函数9. CExample(int b)10. { a = b;}11.12. //⼀般函数13. void Show ()14. {15. cout<<a<<endl;16. }17. };18.19. int main()20. {21. CExample A(100);22. CExample B = A; //注意这⾥的对象初始化要调⽤拷贝构造函数,⽽⾮赋值23. B.Show ();24. return 0;25. }运⾏程序,屏幕输出100。

从以上代码的运⾏结果可以看出,系统为对象 B 分配了内存并完成了与对象 A 的复制过程。

就类对象⽽⾔,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。

下⾯举例说明拷贝构造函数的⼯作过程。

[c-sharp]1. #include <iostream>2. using namespace std;3.4. class CExample {5. private:6. int a;7. public:8. //构造函数9. CExample(int b)10. { a = b;}11.12. //拷贝构造函数13. CExample(const CExample& C)14. {15. a = C.a;16. }17.18. //⼀般函数19. void Show ()20. {21. cout<<a<<endl;22. }23. };24.25. int main()26. {27. CExample A(100);28. CExample B = A; // CExample B(A); 也是⼀样的29. B.Show ();30. return 0;31. }CExample(const CExample& C) 就是我们⾃定义的拷贝构造函数。

14堆与拷贝构造函数

14堆与拷贝构造函数

一、关于堆二、需要new和delete的原因三、分配堆对象四、拷贝构造函数五、默认拷贝构造函数六、浅拷贝与深拷贝七、临时对象八、无名对象九、构造函数用于类型转换为运行函数而分配的局部变量、函数参数、返回数据、返回地址等存放在栈区。

余下的空间都被作为堆区(动态分配的内存)。

在C++用new代替malloc()函数的一个原因是,在分配空间的时候malloc()函数不能调用构造函数。

void fn(){Tdate * pD; //仅仅是个指针,没有产生对象pD=(Tdate*)malloc(sizeof Tdate); //并不调用构造函数//…free(pD); //并不调用析构函数}此时pD是一个含有类对象空间,对应的对象空间中的值不确定。

为此,内存分配之后再进行初始化。

例如,下面的代码描述用malloc()来进行对象的创建过程:void fn(){Tdate * pD;pD=(Tdate*)malloc(sizeof Tdate);pD->SetDate(); //设置Tdate值//…free(pD);}从根本上说,不是类对象的创建,因为它绕过了构造函数看一下下面代码与前面的代码做一比较:void fn(){Tdate * pS;pS=new Tdate; //分配堆空间并构造它//…delete pS; //先析构,然后将空间返还给堆}不必显式指出从new返回的指针类型,因为new必须知道对象的类型(他要藉此调用构造函数)如果分配局部对象:则在该局部对象退出作用域时(要么程序遇到函数结束标记”}”,要么遇到返回语句)自函数动调用。

参考资料1:栈空间和堆空间一直都把堆栈放一起,所以很多人会误以为他们的组合是一个词语,就像“衣服”一样简单,其实不然,今天在下就将最近学习总结的一些与大家分享。

一个由C/C++编译的程序占用的内存分为以下几个部分:OS回收,值得注意的是他与数据结构的堆是两回事,分配方式倒是类似于数据结构的链表。

通常拷贝构造函数的参数

通常拷贝构造函数的参数

通常拷贝构造函数的参数一、概述在面向对象编程中,拷贝构造函数是一种特殊的构造函数,用于创建一个对象的副本。

通常情况下,拷贝构造函数的参数是一个常量引用,用于接收被拷贝对象的引用。

拷贝构造函数以这种方式接受参数是为了避免对被拷贝对象进行修改,并且可以处理传递给构造函数的临时对象。

二、拷贝构造函数的定义拷贝构造函数是一个特殊的成员函数,它具有以下特点:1.函数名与类名相同;2.参数为一个常量引用,用于接受被拷贝对象;3.无返回值;4.通常在公有部分声明,以便其他函数能够调用。

拷贝构造函数的定义格式如下:ClassName(const ClassName &obj) {// 拷贝对象的成员变量到新对象// ...}三、默认拷贝构造函数如果用户没有显式地定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。

默认的拷贝构造函数会逐个拷贝对象的非静态数据成员,这种拷贝方式被称为浅拷贝。

但是,默认的拷贝构造函数可能无法满足所有情况,特别是当类中包含指针或资源时,使用默认的拷贝构造函数可能会导致内存泄漏和二次释放等问题。

因此,在这些情况下,我们需要显式地定义拷贝构造函数。

四、拷贝构造函数的作用拷贝构造函数的主要作用是创建一个对象的副本。

通过拷贝构造函数,我们可以将一个对象的值赋给另一个对象,而不是共享同一个对象。

拷贝构造函数通常在以下场景中使用:1.将一个对象作为参数传递给函数时,使用拷贝构造函数创建函数的局部副本;2.在函数返回时,通过拷贝构造函数将局部对象复制到函数外部;3.在使用一个对象初始化另一个对象时。

五、拷贝构造函数的调用时机拷贝构造函数会在以下情况下被自动调用:1.使用一个对象初始化另一个对象;2.将一个对象作为函数参数传递给函数;3.从函数中返回一个对象。

六、拷贝构造函数的参数类型拷贝构造函数的参数类型通常为常量引用,这是为了避免对被拷贝对象进行修改。

常量引用作为参数的另一个好处是,它可以接受临时对象,这样我们可以使用临时对象来初始化新对象。

C++拷贝构造函数详解

C++拷贝构造函数详解

C++拷贝构造函数详解C++拷贝构造函数详解简介拷贝构造函数是C++独有的⼀种特殊的构造函数,以同型对象初始化⾃我对象。

拷贝构造函数是⼀种特殊的构造函数,具有单个形参,该形参(常⽤const修饰)是对该类类型的引⽤。

当定义⼀个新对象并⽤⼀个同类型的对象对它进⾏初始化时,将显⽰使⽤拷贝构造函数。

当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式调⽤拷贝构造函数。

拷贝构造函数拷贝构造函数是⼀种特殊的构造函数,它在创建对象时,是使⽤同⼀类中之前创建的对象来初始化新创建的对象。

拷贝构造函数通常⽤于:通过使⽤另⼀个同类型的对象来初始化新创建的对象。

复制对象把它作为参数传递给函数。

复制对象,并从函数返回这个对象。

如果⽤户没有定义拷贝构造函数,但是调⽤了拷贝构造函数,那么编译器会⾃动⽣成⼀个默认的拷贝构造函数。

但是如果⾃⼰定义了拷贝构造函数,编译器则不在⽣成。

最常见的形式如下:classname (const classname& obj){// code here}下⾯对三种调⽤拷贝构造函数的情况进⾏⼀⼀说明:通过使⽤另⼀个同类型的对象来初始化新创建的对象#include <iostream>using namespace std;class Student {public:Student(); // default构造函数Student(const Student& obj); // 拷贝构造函数int getNumber();private:int number;};// 定义默认构造函数Student::Student(){this->number = 0;cout << "default constructor" << endl;}// 定义拷贝构造函数Student::Student(const Student& obj) {this->number = obj.number;cout << "copy constructor" << endl;}int Student::getNumber() {return this->number;}int main(){Student s1; // 调⽤默认构造函数Student s2(s1); // 调⽤拷贝构造函数Student s3 = s2; // 调⽤拷贝构造函数cout << s1.getNumber() << endl;cout << s2.getNumber() << endl;cout << s3.getNumber();return 0;}/*Outputdefault constructorcopy constructorcopy constructor*/这⾥创建了三个对象,s1 s2 s3。

拷贝构造函数

拷贝构造函数
C++的拷贝构造函数
深入理解拷贝构造函数
版权声明

该文档参考了郑秋生主编的《C/C++程序设 计教程-----面向对象分册》,并加入了本人 自己的理解,如果有错误或者理解不恰当 的地方,还望指出
拷贝构造函数


C++允许同型变量之间的赋值,例如,int a = 100; int b = a; 对象是一种复杂的类类型,很多时候我们 需要用一个对象去创建另外一个对象,或 者将一个对象赋值给另一个对象,这时就 需要用到拷贝构造函数。




#endif
Complex.cpp

#include"Complex.h" #include<iostream.h> Complex::Complex(double r,double i):real(r),image(i) { cout<<"调用两个参数的构造函数"<<endl; } Complex::Complex(Complex &c) { real = c.real; image = c.image; cout<<"调用拷贝构造函数"<<endl; }






Complex.cpp



void Complex::print(void) { cout<<"("<<real<<","<<image<<")"<<endl; }

第14章堆与拷贝构造函数

第14章堆与拷贝构造函数

Constructing new student Randy Calling fn() Constructing copy of Randy In function fn() Destructing copy of Randy Return from fn() Destructing Randy
默认拷贝构造函数
14.2不能使用malloc()和free()原因
类对象的建立是分配空间、构造结构以及初始化的三位一体,它们 统一由构造函数来完成。
14.3 分配堆对象
C++的new和delete机制更简单易懂。 void fn() { Tdate* pS; pS=new Tdate; //分配堆空间并构造它 //... delete ps; //先析构,然后将空间返还给堆 }
类定义中,如果未提供自己的拷贝构造函数, 则C++ 提供一个默认拷贝构造函数。 C++提供的默认拷贝构造函数工作的方法是, 完 成一个成员一个成员的拷贝。如果成员是类对象, 则调用其拷贝构造函数或者默认拷贝构造函数。
例 14-2
默认拷贝构造函数的使用
#include <iostream.h> #include <string.h> class Student{ public: Student(char* pName="no name") { cout <<"Constructing new student " <<pName <<endl; strncpy(name,pName,sizeof(name)); name[sizeof(name)-1]='\0'; }

python 拷贝构造函数

python 拷贝构造函数

python 拷贝构造函数Python 是一种高级编程语言,它有非常强大的数据类型,这使得 Python 编程中的复制非常关键。

Python 中常常需要执行将一个对象的值复制到另一个对象中的操作,有时候我们还需要创建一个新的对象,这个对象与原始对象具有相同的数据结构,但是各自彼此独立。

Python 中提供了一些内置的方法来实现这些操作,而 Python 拷贝构造函数是其中一种方法。

什么是 Python 拷贝构造函数?Python 拷贝构造函数,也就是 copy constructor,是一个特殊的构造函数。

它用于在创建类的新对象时将已存在的对象的值复制到新对象中,同时保持原始对象不受影响。

使用这个函数可以帮助程序员在保持数据结构不变的同时,节省时间和代码的开发量。

Python 拷贝构造函数的语法如下:class ClassName: # Constructor def __init__(self, arg1, arg2): self.arg1 = arg1 self.arg2 = arg2# Copy constructor def__init__(self, arg): self.arg1 =arg.arg1 self.arg2 = arg.arg2这里需要注意的是在这个例子中,我们使用了两个__init__() 方法来实现拷贝构造函数。

这是因为 Python 不像 C++ 和 Java 一样提供了拷贝构造函数的内置功能,因此我们需要手动实现这个功能。

Python 拷贝构造函数的实现原理Python 拷贝构造函数是通过复制一个对象并在其上进行操作来实现的。

当我们复制一个 Python 对象时,会创建一个新的对象并将原始对象的值复制到新的对象中。

这样,我们就可以在不影响原始对象的情况下对复制对象进行操作和修改。

在 Python 中,我们可以使用两种方式实现拷贝构造函数:浅拷贝和深拷贝。

浅拷贝中,只复制了原始对象的引用,而不是它的值。

堆与拷贝构造函数

堆与拷贝构造函数

student.h#ifndef STUDENT_H#define STUDENT_Hclass Student{public:Student(char* pName="no name",int ssID=0);Student(Student& s);//拷贝构造函数~Student();protected:char name[40];int id;};#endifstudent.cpp#include <iostream>#include <string>#include "student.h"using namespace std;Student::Student(char* pName,int ssID){strcpy(name,pName);cout<<"constructing new student"<<pName<<endl;}Student::Student(Student& s)//拷贝构造函数,必须用引用或指针,不能直接用student(student s) //因为不能像基本数据类型那样直接传递对象的值。

{cout<<"constructing copy of"<<<<endl;strcpy(name,"copy of");strcat(name,);id=s.id;}Student::~Student(){cout<<"destructing"<<name<<endl;}/**test.cpp*堆与拷贝构造函数的测试*2011/10/7*刘珅珅*/#include <iostream>#include <string>#include "student.h"using namespace std;void fn(Student s);int main(int argc,char* argv[]){Student randy("Randy",1234);cout<<"calling fn()\n";fn(randy);//要调用拷贝构造函数cout<<"returned from fn()\n";Student jenny=randy;//调用拷贝构造函数return 0;}void fn(Student s){cout<<"In function fn()\n";}输出的结果:有拷贝构造函数:constructing new studentRandycalling fn()constructing copy of RandyIn functiong fn()destructingcopy ofRandy//fn()函数结束,发生析构,析构的是fn中s对象returned from fn()constructing copy of Randy//拷贝构造jenny对象destructingcopy ofRandy//析构jenny对象destructingRandy//析构了randy对象没有拷贝构造函数:constructing new studentRandycalling fn()In functiong fn()//当没有自定义拷贝构造函数时,C++会调用默认的拷贝构造函数//但类拥有资源时,采用默认的拷贝构造函数容易出问题destructingRandy//fn()函数,发生析构,析构对象sreturned from fn()destructingRandy//析构对象jennydestructingRandy用一个类对象区构造一个新的对象时,如果没有自定义的拷贝构造函数,C++会调用默认的拷贝构造函数,但如果构造函数中有资源(堆内存)时,会出问题,因为默认的拷贝构造函数只会简单的拷贝资源,而不会分配资源,这时会出现:两个对象都拥有同一个资源的局面,在析构时会产生错误。

拷贝构造函数与默认拷贝构造函数

拷贝构造函数与默认拷贝构造函数

拷贝构造函数与默认拷贝构造函数#include <iostream>#include <string.h>using namespace std;class Student{public:Student(char *pName = "no name", int ssId = 0){id = ssId;strcpy(name,pName);cout<<"constructing new student "<<name<<endl;}Student(Student &s){cout<<"constructing copy of "<<<<endl;strcpy(name,"copy of ");strcat(name,);id = s.id;}~Student(){cout<<"Destructing "<<name<<endl;}protected:char name[40];int id;};void fn(Student s){cout<<"In function fn()\n";}class Tutor{public:Tutor(Student &s):student(s){cout<<"Construction tutor"<<endl;}protected:Student student;};/*调⽤默认拷贝构造函数,完成⼀个成员⼀个成员的拷贝.如果成员是类对象,则调⽤其拷贝构造函数或者默认拷贝构造函数*/void fn2(Tutor tutor){cout<<"In function fn2()"<<endl;}void main(){//Student randy("randy",1234);//cout<<"Calling fn()"<<endl;//fn(randy);//cout<<"Returned from fn()"<<endl;Student randy("randy",1234);Tutor tutor(randy);cout<<"Calling fn2()"<<endl;fn2(tutor);cout<<"Returned from fn2()"<<endl;}。

C++拷贝构造函数

C++拷贝构造函数

C++拷贝构造函数拷贝构造函数:以拷贝的⽅式初始化⼀个对象时,会调⽤拷贝构造函数。

拷贝构造函数只有⼀个参数,它的类型是当前类的const引⽤,且任何额外参数都有默认值。

#include <iostream>#include <string>using namespace std;class Student{public:Student(string name = "", int age = 0, float score = 0.0f); //普通构造函数Student(const Student &stu); //拷贝构造函数(声明)public:void display();private:string m_name;int m_age;float m_score;};Student::Student(string name, int age, float score): m_name(name), m_age(age), m_score(score){ }//拷贝构造函数(定义)Student::Student(const Student &stu){this->m_name = stu.m_name;this->m_age = stu.m_age;this->m_score = stu.m_score;cout<<"Copy constructor was called."<<endl;}void Student::display(){cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;}int main(){Student stu1("⼩⽩", 16, 90.5);Student stu2 = stu1; //调⽤拷贝构造函数Student stu3(stu1); //调⽤拷贝构造函数stu1.display();stu2.display();stu3.display();return 0;}为什么必须是当前类的引⽤呢?如果拷贝构造函数的参数是当前类的对象,那么在调⽤拷贝构造函数时,会将另外⼀个对象直接传递给形参,这本⾝就是⼀次拷贝,会再次调⽤拷贝构造函数,陷⼊死循环。

14 堆与拷贝构造函数 2-优质课件

14 堆与拷贝构造函数 2-优质课件

2、需要new和delete的原因
n 在C++用new代替malloc()函数的一个原因是, 它在分配空间的时候不能调用构造函数。类对象 的建立是分配空间、构造结构以及初始化的三位 一体,他们统一由构造函数来完成。
n
例如,下面的代码用malloc()分配对象空间:
n
class Tdate
n
{ public:
C ++ 程序设计教程(修订版)
第十四章 堆与拷贝构造函数
2019年11月23日11时27分
清华大学出版社 钱 能
第十四章 堆与拷贝构造函数
2019年11月23日11时27分
关于堆 需要new和delete的原因 分配堆对象 拷贝构造函数 默认拷贝构造函数 浅拷贝与深拷贝 临时对象 无名对象 构造函数用于类型转换

class A{ private: int *p;

public:

A(int i){ p=new int(i); cout<<"new..."<<endl; }

~A(){ cout<<"delete p..."<<endl; delete p; }

};

int main()

{ A a1(5); //调用构造函数
2019年11月23日11时27分
5、默认拷贝构造函数
如果不定义拷贝构造函数,系统自动为类提供一个缺省的拷贝构造函数(逐一拷贝数据成员 )。
一般情况下,不必定义拷贝构造函数。但是,如果构造函数中存在动态分配,则必须定义拷 贝构造函数

堆与拷贝构造函数

堆与拷贝构造函数

4.5 深拷贝和浅拷贝
如果创建一个对象时, 如果创建一个对象时,分配了 资源就需要定义自己的拷贝构造 函数来改变缺省的逐成员拷贝的 方式,不但拷贝成员, 方式,不但拷贝成员,也拷贝资 源,这种方式称为深拷贝。通常 这种方式称为深拷贝。 深拷贝 ,如果类需要析构函数来释放资 源的话, 源的话,那么它也需要一个拷贝 构造函数。 构造函数。
4.4 拷贝构造函数
普通构造函数在对象创建时被调用, 普通构造函数在对象创建时被调用,拷贝构造函数在以下三种情况下会 被调用: 被调用: 1. 用类的一个对象去初始化该类的另外一个对象,例如: 用类的一个对象去初始化该类的另外一个对象,例如: void main() { TPoint A(1,2); TPoint B(A); //用对象 初始化对象 ,拷贝构造函数被调用 用对象A初始化对象 用对象 初始化对象B, //… } 2. 函数的形参是类的对象,调用函数进行形参和实参结合时,例如 函数的形参是类的对象,调用函数进行形参和实参结合时, void f (TPoint p) //形参 用实参的值进行构造 形参p用实参的值进行构造 形参 { cout<<p.GetX ( )<<endl; }
4.3 分配堆对象
C++的new和delete机制更简单易懂。 的 机制更简单易懂。 和 机制更简单易懂
void fn() { Tdate * pS; pS=new TDate; //分配堆空间并构造 分配堆空间并构造 //… delete pS; //析构并将空间返还给堆 析构并将空间返还给堆 }
1.
全局数据区 代码区 栈区 堆区
2.
3.
4.
全局变量、静态数据、常量存放在全局数据区; 全局变量、静态数据、常量存放在全局数据区;所有类的成 员函数和非成员函数代码存放在代码区; 员函数和非成员函数代码存放在代码区;为运行函数而分配的局 部变量、函数参数、返回数据、返回地址等存放于栈区; 部变量、函数参数、返回数据、返回地址等存放于栈区;余下的 空间作为堆区。 空间作为堆区。

《堆与复制构造函数》PPT课件

《堆与复制构造函数》PPT课件
精选ppt17第一个问题的答案首先在调用函数的时候程序创建了一个对象的副本作为形式参数此时普通的构造函数normalconstructor并没有被调用而是复制构造函数copyconstructor被调精选ppt18为什么需要复制构造函数由于普通的构造函数通常用于初始化对象的某些成员因此就不能调用普通构造函数创建对象的副本因为这样产生的对象可能与现有的对象不完全相同
Square s(10);
f(s); //对象s以传值方式传送给临时对象ob
s.display();
return 0;
}
该程序运行后的输出结果如下:
Constructiong
Copy Constructiong
10
Destructiong
10
Destructiong
精选PPT
24
自定义复制构造函数
void display() { cout << side << "\n"; }
};
精选PPT
11
【例5.1】在堆上创建对象(续)
int main() {
Square *ps=new Square(10); //分配堆内存并调用构造函数初始化 ps->display();
delete ps; //自动调用析构函数,然后释放堆内存
void display(my_string &ob)
{
ob.show( );
避免复制对象
}
经过修改后,程序运行后的输出结果如下:
Allocating room for s
Hello!
Hello!
Freeing s
精选PPT
31
初始化(initialization)

拷贝构造函数与移动构造函数

拷贝构造函数与移动构造函数

拷贝构造函数与移动构造函数拷贝构造函数与移动构造函数
拷贝构造函数与移动构造函数的区别
拷贝构造函数的形参是⼀个左值引⽤
拷贝构造函数完成的是整个对象或变量的拷贝


移动构造函数的形参是⼀个右值引⽤
移动构造函数是⽣成⼀个指针指向源对象或变量的地址,接管源对象的内存,相对于⼤量数据的拷贝节省时间和内存空间。

参考
Test(Test&& T):m_ptr(T.m_ptr) {
T.m_ptr = nullptr;
cout << "移动拷贝构造" << endl;
}
Test(const Test& T){
m_ptr=new int;
*m_ptr=*T.m_ptr;
cout << "常引⽤深拷贝构造" << endl;
}
Test(Test& T){
m_ptr=T.m_ptr;//error
cout << "浅拷贝构造" << endl;
}
~Test(){
delete m_ptr;
}
第三个之所以不能⽤是应该第⼀个析构把T.m_ptr delete了,第⼆次析构m_ptr时,这两个指针指向同⼀个地址了,所以err。

拷贝构造和拷贝赋值

拷贝构造和拷贝赋值

拷贝构造和拷贝赋值拷贝构造和拷贝赋值是面向对象编程中常用的两个概念。

它们在C++、Java等编程语言中经常被用到,对于理解对象的复制和拷贝有着重要的作用。

拷贝构造是指通过已存在的对象创建一个新的对象,这个新对象和原对象的成员变量具有相同的值。

例如,我们有一个学生类(Student),其中包含学生的姓名、学号、年龄等成员变量。

当我们通过已存在的学生对象创建一个新的学生对象时,就需要使用拷贝构造。

拷贝构造函数在C++中常用的格式为:类名(const 类名& obj)。

在拷贝构造函数中,我们将传递进来的对象的成员变量值赋值给新创建的对象的成员变量。

拷贝构造函数的使用可以大大简化对象的创建过程,并且保证了新创建的对象和原对象之间的数据一致性。

同时,拷贝构造函数还可以用于对象作为函数参数进行传递,以及对象作为函数返回值返回的情况。

拷贝赋值是指将一个已存在的对象的值赋给另一个已经存在的对象。

在C++中,我们可以通过重载赋值操作符(=)来实现对象的拷贝赋值。

拷贝赋值的格式为:类名& operator=(const 类名& obj)。

在拷贝赋值中,我们将被赋值对象的成员变量值赋给目标对象的成员变量。

拷贝赋值操作符的使用可以方便地将一个对象的值拷贝给另一个对象,使得两个对象具有相同的值。

拷贝赋值操作符还可以用于链式赋值,即多个赋值操作符连续使用的情况。

无论是拷贝构造还是拷贝赋值,都是对象间的复制操作,但它们的触发时机和方式是不同的。

拷贝构造是在创建新对象时调用,而拷贝赋值是在已有对象进行赋值操作时调用。

需要注意的是,在使用拷贝构造和拷贝赋值的过程中,我们需要自行管理动态分配的资源(如堆内存),防止内存泄漏和悬垂指针的问题。

拷贝构造和拷贝赋值在实际的编程中有着广泛的应用。

它们不仅可以用于类的对象,还可以用于容器类、智能指针等等。

对于理解对象的复制、赋值和传递过程,以及保证程序的正确性和高效性,拷贝构造和拷贝赋值具有重要的指导意义。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

第14章堆与拷贝构造函数在C++中,堆分配的概念得到了扩展,不仅C++的关键字new和delete可以分配和释放堆空间,而且通过new建立的对象要调用构造函数,通过delete删除对象也要调用析构函数。

另外,当对象被传递给函数或者对象从函数返回的时候,会发生对象的拷贝。

但有些情况,一模一样的拷贝并不是所希望的,这就要借助于定义拷贝构造函数了。

学习本章后,应该掌握new和delete这两个操作符的使用,并能把握从堆中分配和释放对象以及对象数组的时机;领会拷贝构造函数的实质,区别浅拷贝和深拷贝,在程序中适当地运用拷贝构造函数。

14.1 关于堆C++程序的内存格局通常分为四个区(1)全局数据区(data area);(2)代码区(code area);(3)栈区(stack area);(4)堆区(即自由存储区)(heap area)。

全局变量、静态数据、常量存放在全局数据区,所有类成员函数和非成员函数代码存放在代码区,为运行函数而分配的局部变量、函数参数、返回数据、返回地址等存放在栈区,余下的空间都被作为堆区。

函数"void* malloc(size-t);"和"void free(void*);"在头文件malloc.h中声明,而操作符new和delete是C++语言的一部分,无须包含头文件。

它们都从堆中分配和释放内存块,但在具体操作上两者有很大的区别。

操作堆内存时,如果分配了内存,就有责任回收它,否则运行的程序将会造成内存泄漏。

这与函数中在栈区分配局部变量有本质的不同。

对C++来说,管理堆区是一件十分复杂的工作,频繁地分配和释放不同大小的堆空间,将会产生堆内碎块。

14.2 需要new和delete的原因从C+十的立场上看,不能用malloc()函数的一个原因是,它在分配空间的时候不能调用构造函数。

类对象的建立是分配空间、构造结构以及初始化的三位一体,它们统一由构造函数来完成。

例如,下面的代码用malloc()分配对象空间:class Tdate{public:Tdate();SetDate(int m=1,int d=l,int y=1998);protected:int month;int day;int year;};Tdate::Tdate(){month=1;day=1;year=l;}voidTdate::SetDate(int m,int d, int y){if(m>0 && m<13)month=m;if(d>O && d<32)day=d;if(y>0 && y<3000)year=y;}void fn(){Tdate* pD; //仅仅是个指针,没有产生对象pD=(Tdate*)malloc(sizeof Tdate); //并不调用构造函数//...free(pD); //并不调用析构函数}指针pD的声明不为Tdate调用其构造函数,因为pD没有指向任何东西。

假如构造函数要被调用,则必须在进行内存分配的ma]loc()调用时进行。

然而malloc()仅仅只是一个函数调用,它没有足够的信息来调用一个构造函数,它所接受的参数是一个unsigned long类型。

pD从malloc()那儿获得的不过是一个含有非法数据的类对象空间而已,对应的对象空间中的值不确定。

为此,须在内存分配之后再进行初始化。

例如,下面的代码描述用malloc()来进行对象的创建过程:void fn(){Tdate*pD;pD=(Tdate*)malloc(sizeof Tdate);pD->SetDate(); //设置Tdate值//...free(pD);}这从根本上说,不是一个类对象的创建,因为它绕过了构造函数。

另外,从程序设计的需要来看,在分配内存申请的时候,总是知道分配的空间派什么用,而且分配空间大小总是某个数据类型(包括类类型)的整数倍。

因而C++用new代替C的malloc()是必然的。

14.3分配堆对象C++的new和delete机制更简单易懂。

例如,下面的代码可与前面的代码做一比较:void fn(){Tdate* pS;pS=new Tdate; //分配堆空间并构造它//...deleteps; //先析构,然后将空间返还给堆}不必显式指出从new返回的指针类型,因为new知道要分配对象的类型是Tdate。

而且new还必须知道对象的类型,因为它要藉此调用构造函数。

如果是分配局部对象,则在该局部对象退出作用域时(要么程序执行遇到函数结束标记"|",要么遇到返回语句)自动调用析构函数。

但是堆对象的作用域是整个程序生命期,所以除非程序运行完毕,否则堆对象作用域不会到期。

堆对象析构是在释放堆对象语句delete 执行之时。

上面的堆对象在执行delete pS;语句时,C++自动调用其析构函数。

构造函数可以有参数,所以跟在new后面的类类型也可以跟参数。

例如下面的代码,new后面的类型必须跟参数:class Tdate{public:Tdate(int m,int d,int y);protected:int month;int day;int year;};Tdate::Tdate(){if(m>0 && m<13)month=m;if(d>0 && d<32)day=d;if(y>0 && y<3000)year=y;}void fn(){Tdate* pD;pD=new Tdate(1,1,1998);//...delete(pD);}"pD=newTdate(1,1,1998);"这一名,使new去调用了构造函数Tdate(int,int,int),new是根据参数匹配的原则来调用构造函数的。

如果上一句写成:pD=new Tdate;则由于Tdate类没有默认构造函数(已被Tdate(int,int,int)覆盖)而使该语句报错。

从堆中还可以分配对象数组。

例如,下面的代码分配了参数给定的对象个数,并在函数结束时,予以返还:class Student{public:Student(char* pName="no name"){strncpy(name,pName,sizeof(name));name[sizeof(name)-1]="\0";}protected:char name[40];};void fn(int noOfObjects){Student* pS=new Student[noOfObjects];//...delete[]pS;}分配过程将激发noOfObjects次构造函数的调用,从0~noOfObjects-1。

调用构造函数的顺序依次为pS[0],pS[1],pS[2],…pS[noOfObjects-1]。

由于分配数组时,new的格式是类型后面跟[元素个数],不能再跟构造函数参数,所以,从堆上分配对象数组,只能调用默认的构造函数,不能调用其他任何构造函数。

如果该类没有默认构造函数,则不能分配对象数组。

delete[]pS中的[]是要告诉C++,该指针指向的是一个数组。

如果在[]中填上了数组的长度信息,C++编译系统将忽略,并把它作为[]对待。

但如果忘了写[],则程序将会产生运行错误。

一般来说,堆空间相对其他内存空间比较空闲,随要随拿,给程序运行带来了较大的自由度。

使用堆空间往往由于:(1)直到运行时才能知道需要多少对象空间;(2)不知道对象的生存期到底有多长;(3)直到运行时才知道一个对象需要多少内存空间。

14.4 拷贝构造函数可用一个对象去构造另一个对象,或者说,用另一个对象值初始化一个新构造的对象例如:Student S1("Jenny");Student s2=sl; //用s1的值去初始化s2对象作为函数参数传递时,也要涉及对象的拷贝,例如:void fn(Student fs){//...}void main(){Student ms;fn(ms);}函数fn()的参数传递的方式是传值,参数类型是Student,调用时,实参ms传给了形参fs,ms在传递的过程中是不会改变的,形参fs是ms的一个拷贝。

这一切是在调用的开始完成的,也就是说,形参fs用ms的值进行构造。

这时候,调用构造函数Student(char*)就不合适,新的构造函数的参数应是Student&,也就是:Student(Student& S);为什么C++要用上面的拷贝构造函数,而它自己不会做像下面的事呢?即:int a:5;intba; //用a的值拷贝给新创建的b因为对象的类型多种多样,不像基本数据类型这么简单,有些对象还申请了系统资源,如图14-1所示,s对象拥有了一个资源,用s的值创建一个t对象,如果仅仅只是二进制内存空间上的s拷贝,那意味着t也拥有这个资源了。

由于资源归属权不清,将引起资源管理的混乱。

在14.6节中还要对这个问题展开讨论。

图14-1 T对象创建时拷贝S对象下面的程序介绍了拷贝构造函数的用法://**********************//** ch14_1.cpp **//**********************#include <iostream.h>#include <string.h>class Student{public:Student(char* pName="no name",int ssId=0){strncpy(name,pName,40);name[39]='\0';id = ssId;cout <<"Constructing new student " <<pName <<endl;}Student(Student& s) //拷贝构造函数{cout<<"Constructing copy of"<<<<endl;strcpy(name,"copy of ");strcat(name,);id=s.id;}~Student(){cout <<"Destructing " <<name <<endl;}protected:char name[40];int id;};void fn(Student s){cout <<"In function fn()\n";}void main(){Student randy("Randy",1234);cout <<"Calling fn()\n";fn(randy);cout <<"Returned from fn()\n";}运行结果为:Constructing new student RandyCalling fn()Constructing copy of RandyIn function fn()Destructing copy of RandyReturn from fn()Destructing Randyrandy对象的创建调用了普通的构造函数,产生了第一行信息;随之便输出第二行信息;main()调用fn(randy)时,发生了从实参randy到形参s的拷贝构造,于是调用拷贝构造函数而得到第三行信息;随之就进入到fn()的函数体中,产生了第四行信息;从fn()返回时,形参s 被析构,所以产生了第五行信息;回到主函数后,输出第六行信息;最后主函数结束时,randy 对象被析构,所以产生了第七行信息。

相关文档
最新文档