首页 > 编程知识 正文

拷贝构造函数和赋值构造函数,赋值构造函数和拷贝构造函数区别

时间:2023-05-06 03:45:10 阅读:157756 作者:4733

C的初学者很了解复制构造函数。 我迷上了复制构造函数和赋值函数。 因为很闲,所以组织起来就是一个对象的复制构造函数和赋值构造函数。 总体而言,复制构造函数和赋值构造函数的共同点是赋值运算符和复制构造函数都使用现有的b对象创建另一个对象a。

最大的不同在于赋值构造函数处理两个现有对象。 也就是说,在代入前应该存在b。 复制构造函数将生成一个新对象,a在调用复制构造函数之前不存在。

- 类处理问题的抽象描述,集数据(事物的属性)和函数(事物的动作/操作)于一体。

类的定义格式如下。

类名(private ) /私有数据和函数(protect ) /保护数据和函数(public ) /公共数据和函数) c中面向对象的思想是操纵对象的属性(成员变量)

顺便说一下,成员函数定义的一般格式如下。

范围值类型类名:3360函数名(参数表)//函数体)基础差的程序员很多,“:3360”符号不知道意思(虽然知道也没办法),只知道使用方法! 运算符" : "是作用域分析运算符,指示函数是哪个类的成员函数。 当然,也可以直接在类的定义中定义函数。 所以,有时也不用“:”的符号。

也就是说,类的实例。 可以通过以下两种方法创建类的对象:

1、CObject类对象直接创建对象,如CObject object (请注意,此处的object是临时对象)

2 .采用动态创建类对象的方法,当然也可以动态创建遍历。

虽然在- 复制构造函数上讨论了类和对象的创建,但是同一个类的对象在内存中具有完全相同的结构,作为整体复制是完全可以的。 复制过程只需复制数据成员,但函数成员可以共享,同一类中的任何对象都可以共享该类的函数成员。

创建对象时,可以用同一类中的另一个对象初始化对象。 此时使用的构造函数是复制构造函数。 格式类似于x:3360x(x ),只有一个参数——类的对象,采用的是引用方法。 不能使用名为x:3360x(x )的构造函数。 如果将真正类的对象作为参数传递给复制构造函数,则会发生无限递归。 虽然很多人都很困惑,但是平时我创建类的对象时也没有写复制构造函数。 为什么没有报告错误呢? 这是因为,如果未定义,编译器将生成缺省的复制构造函数; 如果定义了自己的复制构造函数,则不存在缺省复制构造函数。 (这一点与析构函数不同,即使您自己定义析构函数,默认析构函数仍然存在。)

在以下三种情况下调用构造函数:

使用一个对象初始化同类型的另一个对象时。 cobjectO2(O1; //CObject o2=o1; 如果某个函数包含作为a类对象的s形参数,则调用该函数时将调用a类复制构造函数。 voidfun(cobjectobject ) { object.a=1; }对象; fun (对象; 调用//CObject的复制构造函数以生成波形参数,在内存中创建新的本地对象,并将实际参数复制到新对象中。 如果函数的返回值是CObject类的对象,则在函数返回时调用CObject的复制构造函数。 理由也是创建临时对象并返回调用方。 CObject Fun () { cobject对象; 返回对象; //其中cobject(object ) }int main ) ) cobject(object; //这里是本地对象,也是临时对象的object=Fun (; 返回0; }为什么不直接使用返回的局部对象? 你可能会问。 本地对象在离开创建它的函数后会消失,因此在调用函数后无法继续生存。 因此,编译系统会在调用函数的表达式中创建一个无名临时对象。 此临时对象的生存期仅位于函数调用的表达式中。 返回对象实际上是调用复制构造函数将该对象的值复制到临时对象中。

-赋值构造函数让我们先看看代码。

int main () { CObject theObjectOne; theobjectone.init(100; CObject theObjectTwo; theobjecttwo.init(200; theObjectTwo=theObjectOne; //此操作是一种对象赋值操作,更深入的操作会清除所复制对象theObjectTwo对象的原始内容并填充theObjectOne对象的内容。 返回0; }“=”是指将一个对象的内容告诉另一个对象,其中包括放弃对象的原始内容和复制新内容。 由于对象的内容包含指针,导致不良结果的:指针的值被丢弃。 (指针的内容丢失意味着指针的地址丢失,但更改地址不会丢失存储的内容,但指针指向的内容不会释放。 指针的值被复制了,但是指针指向的内容没有被复制,所以很麻烦。)。 所以如果班级的

对象中有指针的话,此处的“=”绝对不能是我们平常理解的简简单单的等于号,这个“=”必须重载,而这个“=”的重载函数就是我们要讨论的赋值构造函数。

我们再来理解两个概念:值拷贝和位拷贝,位拷贝拷贝的是地址;值拷贝拷贝的是内容。理解了这两个概念我们再看一段代码:
定义一个string类,但是不实现其他的成员函数

Class String{public: String(const char *ch = NULL); //默认构造函数 String(const String &str); //复制构造函数 ~String(); String &operator=(const String &str);//赋值构造函数 privat: char *m_data;}int main(){ String strA; strA.m_data=L"Windows"; String strB; strB.m_data=L"Linux"; strB.m_data = strA.m_data;}

如果“=”未重写赋值构造函数的话,将strA赋给strB;则编译器会默认进行位拷贝,即strB.m_data = strA.m_data;
则strA.m_data和strB.m_data都指向了同一块区域,虽然strA.m_data指向的内容会改变为“Linux”,但是会出现这样的问题:
(1)strB.m_data原来指向的内存区域未释放,造成内存泄露。
(2)strB.m_data和strA.m_data指向同一块区域,任何一方改变都会影响另一方。
(3)当对象被析构时,strA.m_data被释放两次。

如果“=”重写了复制构造函数后,strB.m_data = strA.m_data;进行的是值拷贝,会将strA.m_data的内容赋给strB.m_data,strB.m_data还是指向原来的内存区域,但是其内容改变。

所以在我理解起来,赋值构造函数其实就是对“=”的重载。缺省的赋值构造函数采用的是“位拷贝”而非“值拷贝”的方式实现。如果类中含有指针变量,那么赋值构造函数如果不重载的话肯定会出错。理解到这里,我们再回过头看一下复制构造函数,其实也是一样的道理,缺省的复制构造函数采用的也是“位拷贝”的方式实现,所以如果类中含有指针变量的话我们也需要重写复制构造函数。
总而言之一句话,什么把你弄的这么头疼来看这篇文章,归根到底就是——指针。

最后附上一段不错的理解复制构造函数和赋值构造函数的代码:

#include <iostream>#include <cstring>using namespace std;class String { public: String(const char *str); String(const String &other); String & operator=(const String &other); ~String(void); private: char *m_data;};String::String(const char *str){ cout << "自定义构造函数" << endl; if (str == NULL) { m_data = new char[1]; *m_data = ''; } else { int length = strlen(str); m_data = new char[length + 1]; strcpy(m_data, str); }}String::String(const String &other){ cout << "自定义复制构造函数" << endl; int length = strlen(other.m_data); m_data = new char[length + 1]; strcpy(m_data, other.m_data);}String & String::operator=(const String &other){ cout << "自定义赋值函数" << endl; if (this == &other) { return *this; } else { delete [] m_data; int length = strlen(other.m_data); m_data = new char[length + 1]; strcpy(m_data, other.m_data); return *this; }}String::~String(void){ cout << "自定义析构函数" << endl; delete [] m_data;}int main(){ cout << "a("abc")" << endl; String a("abc"); cout << "b("cde")" << endl; String b("cde"); cout << " d = a" << endl; String d = a; cout << "c(b)" << endl; String c(b); cout << "c = a" << endl; c = a; cout << endl;

执行结果

a(“abc”)
执行自定义构造函数
b(“ced”)
执行自定义构造函数
d=a
执行自定义复制构造函数
c(b)
执行自定义复制构造函数
c=a
执行自定义赋值函数

执行自定义析构函数
执行自定义析构函数
执行自定义析构函数
执行自定义析构函数

说明几点

赋值函数中,上来比较 this == &other 是很必要的,因为防止自复制,这是很危险的,因为下面有delete []m_data,如果提前把m_data给释放了,指针已成野指针,再赋值就错了

赋值函数中,接着要释放掉m_data,否则就没机会了(下边又有新指向了)

拷贝构造函数是对象被创建时调用,赋值函数只能被已经存在了的对象调用

注意:String a(“hello”); String b(“world”); 调用自定义构造函数

String c=a;调用拷贝构造函数,因为c一开始不存在,最好写成String c(a);

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。