c++ 副本构造器

我们都知道两个指针指向同一个变量时如果一个指针被释放那么另一个就会出问题

为了说明问题我做了一个很恶心的小例子


class C
{
public :
C(int v)
{
ptrInt=new int;
*ptrInt=v;

valueInt = v;
}

~C()
{

}
void DelIntV()
{
valueInt=0;
delete ptrInt;
}

C(const C& c)
{

}
int * ptrInt;
int valueInt;
private:

};

int main()
{
C c1(2);
C c2(3);
c2=c1;
std::cout<<"ptrInt "<<c2.ptrInt<<" value "<<*c2.ptrInt<<std::endl;
std::cout<<"valueInt "<<c2.valueInt<<std::endl;
c1.DelIntV();

std::cout<<"address "<<c2.ptrInt<<" value "<<*c2.ptrInt<<std::endl;
std::cout<<"valueInt "<<c2.valueInt<<std::endl;
std::cin.get();
return 0;
}

这是把c1赋值给了c2后把指针ptrInt的值输出和valueInt输出,再把c1的指针给delete,valueInt赋值为0

再输出c2的ptrInt和valueInt就会发现指针有问题,看一下输出结果:

已经不对了吧。

为了解决这样的问题我第一个想到的就是重载操作符=


C& operator=(const C &c)
{
if(this!=&c)
{
delete ptrInt;
ptrInt = new int;
*ptrInt= *c.ptrInt;
valueInt=c.valueInt;
}
return *this;
}

完整代码


class C
{
public :
C(int v)
{
ptrInt=new int;
*ptrInt=v;

valueInt = v;
}

~C()
{

}
void DelIntV()
{
valueInt=0;
delete ptrInt;
}

C(const C& c)
{

}
int * ptrInt;
int valueInt;

C& operator=(const C &c)
{
if(this!=&c)
{
delete ptrInt;
ptrInt = new int;
*ptrInt= *c.ptrInt;
valueInt=c.valueInt;
}
return *this;
}
private:

};

int main()
{
C c1(2);
C c2(3);
c2=c1;
std::cout<<"ptrInt "<<c2.ptrInt<<" value "<<*c2.ptrInt<<std::endl;
std::cout<<"valueInt "<<c2.valueInt<<std::endl;
c1.DelIntV();

std::cout<<"address "<<c2.ptrInt<<" value "<<*c2.ptrInt<<std::endl;
std::cout<<"valueInt "<<c2.valueInt<<std::endl;
std::cin.get();
return 0;
}

再看一下输出结果:

这下就正确了吧,但是如果 我们在main函数里做一个修改


int main()
{
C c1(2);
C c2=c1;//这里直接赋值
std::cout<<"ptrInt "<<c2.ptrInt<<" value "<<*c2.ptrInt<<std::endl;
std::cout<<"valueInt "<<c2.valueInt<<std::endl;
c1.DelIntV();

std::cout<<"address "<<c2.ptrInt<<" value "<<*c2.ptrInt<<std::endl;
std::cout<<"valueInt "<<c2.valueInt<<std::endl;
std::cin.get();
return 0;
}

这样后错误就又和之前一样了,为什么呢,

编译器将在c类里找一个副本构造器(copy constructor)如果找不到它会自己创建一个,

即使我们对操作符=进行了重载也没有用,由编译器自己创建的副本构造器仍会以"逐们复制"

的方式把c1赋值给c2

这样我们还要重新实现这个副本构造器,

className(const className &cn);

我是这样做的

    C(const C& c)
{
*this=c;
}

这里的=其实就是调用的重载的=方法

完整代码


class C
{
public :
C(int v)
{
ptrInt=new int;
*ptrInt=v;

valueInt = v;
}

~C()
{

}
void DelIntV()
{
valueInt=0;
delete ptrInt;
}

C(const C& c)
{
*this=c;
}
int * ptrInt;
int valueInt;

C& operator=(const C &c)
{
if(this!=&c)
{
delete ptrInt;
ptrInt = new int;
*ptrInt= *c.ptrInt;
valueInt=c.valueInt;
}
return *this;
}

private:

};

int main()
{
C c1(2);
C c2=c1;//这里直接赋值
std::cout<<"ptrInt "<<c2.ptrInt<<" value "<<*c2.ptrInt<<std::endl;
std::cout<<"valueInt "<<c2.valueInt<<std::endl;
c1.DelIntV();

std::cout<<"address "<<c2.ptrInt<<" value "<<*c2.ptrInt<<std::endl;
std::cout<<"valueInt "<<c2.valueInt<<std::endl;
std::cin.get();
return 0;
}

结果

c++ 副本构造器

时间: 2024-11-09 06:09:53

c++ 副本构造器的相关文章

c++第十三章 -(副本构造器)

逐位复制(bitwise copy):编译器将生成必要的代码把“源”对象各属性的值分别赋值给“目标”对象的对应成员的行为.对对象的地址赋值操作,于是乎,当删除一个对象时,它包含的指针也将被删除,但万一此时另一个副本(对象)还在引用这个指针,就会出问题! 要是程序员在当初进行对象“复制”时能够精确地表明应该复制些什么和如何赋值,那就理想了.为解决该问题,我们可以对=操作符进行重载,其中对指针进行处理:MyClass &operator= (const MyClass &rhs);该语句的意思

C++学习3

虚继承(virtual inheritance): 语法:class SubClass : virtual public BaseClass{} 虚继承的类(SubClass)没有父类(BaseClass)的副本,在SubClass的子类中,构造函数应该调用BaseClass的构造函数. 程序的错误:编译时错误.运行时错误. 动态内存: 没有名字,只有地址,运行时分配. delete只释放内存,指针仍在,指针值仍在. 数组名和下标操作符的组合可以看成是:数组基地址和对应的指针运算. 从函数或方法

c++第十七章-(内联函数)

1.内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质.编译时,类似宏替换,使用函数体替换调用处的函数名. 一般在代码中用inline修饰,但能否形成内联函数,需要看编译器对该函数定义的具体处理. such as: inline int add(int a,int b,int c) { return a+b+c; } 不管是什么模板,编译器都必须看到全部的代码才能为一种给定的类型创建出一个新的实现来. 内联方法的引入很好的解决了在创建类模板时,避免类声明和类定义相互分离. 在类里

类、方法、构造器、变量介绍

类 类是Java最小单位,方法.构造器.变量都必须在类里. 语法:[修饰符] class  类名{ //多个Field; //多个构造器; //多个方法; } [修饰符]:public | final | abstract  任选一,也可省略. 类名:合法标识符即可,建议是有意义的单词,每个首字母都大写. 方法 方法不能独立存在,要么属于对象,要么属于类,必须使用类或实例作为调用者. 语法:[修饰符] 返回值类型 方法名 (形参列表){        //可执行语句; }修饰符: 可以省略  

js--使用构造器函数来新建对象及操作

通过new操作符来调用函数,来达到访问对象this值得目的,构造器将其创建的对象返回给我们. 直接上代码 //创建构造器函数 function Gadget(name, color){ this.name = name ;//添加属性 this.color = color ; this.whatAreYou = function(){//添加方法 return this.color + " " + this.name ; } } //另一种添加属性和方法,通过构造器函数的prototy

MongoDB副本集

简介 mongodb复制(replication)是将数据同步在多个服务器的过程.主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致.复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并保证数据的安全性.复制还允许您从硬件故障和服务中断中恢复数据. 而副本集(replica set)是从mongodb 1.6 提供的新功能,比复制功能要强大一些并增加了故障自动切换和自动修复成员节点,

MongoDB 搭建副本集

副本集(Replica Set)是一组MongoDB实例组成的集群,由一个主(Primary)服务器和多个备份(Secondary)服务器构成.通过Replication,将数据的更新由Primary推送到其他实例上,在一定的延迟之后,每个MongoDB实例维护相同的数据集副本.通过维护冗余的数据库副本,能够实现数据的异地备份,读写分离和自动故障转移. 一,MongoDB版本和环境 在Windows上创建包含三个节点的副本集,使用的环境: 数据库:MongoDB 版本 3.2.9 Server

MICS:副本和纠删码混合存储系统

摘要 云存储系统的三个指标: 高可靠性,低存储开销,高读写性能. 这三个指标是没有办法同一时候满足的,许多时候须要进行tradeoff. 副本系统和纠删码是两种在存储系统中广泛使用的策略,它们在保证高可靠性的前提下,选择了不同极端的tradeoff. 副本存储开销大,但性能较好.纠删码存储开销低.但性能较差.本文提出了MICS系统.它将一个对象以两种形式存储,一种採用副本.一种採用分片纠删码,不仅如此.还设计了针对这种hyprid结构的精细的读写协议. 在服务使用者的角度.MICS通过PRAM一

Mongodb副本集实现

MongoDB副本集概述 以下图片摘自MongoDB官方文档:http://docs.mongodb.org/manual/core/replication-introduction/ Primary节点接收客户端所有的写操作,整个副本集只会有一个primary节点.MongoDB副本集提供严格的一致性.主节点将所有的操作写入一个叫oplog的capped collection(这个collection的大小一般为磁盘剩余空间的5%,不同的系统可能不一样,详见http://docs.mongod