Bootstrap

C++编程 拷贝(复制)构造函数 案例详细解析

目录

一:公共接口(提供类外接口)

二:拷贝构造

三:拷贝构造函数 基本概念

四:触发拷贝构造的3种情况

1.类对象直接赋值

2.类对象作为函数参数时(建立局部对象)

3.函数返回是类对象时(建立临时对象)

五:类对象作为函数参数

六:类对象作为函数返回

七:拷贝构造(何时使用)

八:函数传参

九:引用传参

十:转换构造函数

十一:C++的几种构造函数

十二:深拷贝 & 浅拷贝

十三:析构函数作用


一:公共接口(提供类外接口)

属性是私有的,若是类外需要使用到,则编写 get,set方法

get方法 return返回,可以在类外部自己获取类的私有成员属性

set方法 赋值,可在类外部自己设置修改类的私有成员属性 对于整数使用=赋值,对于字符数组使用strcpy,但是对于字符数组也可以使用=赋值,需要将char数据类型改为string可=赋值

下面是get/set方法 编写的简单示例 

int getHeight(){return height;}

void setHeight(pheight){height = pheight};

二:拷贝构造

拷贝构造

示例如下 

CFrog.h:

#ifndef CFROG_H
#define CFROG_H

//成员变量 属性:height color name
//成员函数 行为:jump  eat  digist

class Frog
{
public:  
	Frog();  
	Frog(int pheight,char pcolor[],char pname[]);
    Frog(Frog &frog);  //拷贝构造
	~Frog();
    void jump();
	void eat();
	void digist();
	void setName(char *newName);
private:    
	int height;     //高度  
	char color[20]; //颜色  
	char *name;     //名字  
protected:
};

#endif

CFrog.cpp:

#include"CFrog.h"
#include<iostream>
using namespace std;

Frog::Frog()   //构造函数没有函数类型
{
	cout<<"默认构造"<<endl;
	height = 5;
	strcpy(color,"green");
	name = (char *)malloc(20);
	memset(name,0,20);
	strcpy(name,"guagua");
}

Frog::Frog(int pheight,char pcolor[],char pname[])
{
	cout<<"带参构造"<<endl;
	height = pheight;
	strcpy(color,pcolor);
	name = (char *)malloc(20);
	memset(name,0,20);
	strcpy(name,pname);
}

void Frog::jump()
{
	cout<<name<<"jump"<<endl;
}

void Frog::eat()
{
	cout<<name<<"eat"<<endl;
	digist();
}

void Frog::digist()
{
	cout<<"digist"<<endl;
}

//用成员函数去访问成员内部私有变量
//设置名称 重命名
void Frog::setName(char *newName)
{
	name = (char *)malloc(20);
	memset(name,0,20);
	strcpy(name,newName);
}

//拷贝构造函数
Frog::Frog(Frog &frog)  //引用与原变量共享一片内存空间
{
	height = frog.height;
	
	name = (char *)malloc(20);
	memset(name,0,20);
	strcpy(name,frog.name);
	
	strcpy(color,frog.color);
	
	cout<<"拷贝构造"<<endl;
}

Frog::~Frog()
{
	//释放
	free(name);
	cout<<"析构函数"<<endl;
}

main.c:

#include<iostream>  //C++输入输出库
using namespace std; //std标准命名空间
#include"CFrog.h"
#include<conio.h>
#include<ctime>

int main()
{	
	Frog frog2(10,"gray","hama");
	Frog frog3 = frog2;  //类对象赋值  执行拷贝构造
	frog2.jump();
	frog3.jump();

	frog3.setName("xiaogongzhu");
	frog2.jump();  //对象调用成员函数
	frog3.jump();  //对象调用成员函数
	return 0;
}
//带参构造
//拷贝构造
//hamajump
//hamajump
//hamajump
//xiaogongzhujump
//析构函数
//析构函数

三:拷贝构造函数 基本概念

1. 使用一个已经存在的对象来初始化一个新的本类的对象

	Frog frog2(10,"gray","hama");
	Frog frog3 = frog2;  //类对象赋值  执行拷贝构造

2. 拷贝构造函数的声明:只有一个参数并且参数为该类对象的引用

类对象赋值,用已经存在的对象去生成一个新的对象,也就是拷贝构造函数

    Frog(Frog &frog);  //拷贝构造

如果类中没有说明复制构造函数,则系统自动生成一个缺省复制构造函数,作为该类的公有成员

类对象赋值,若是没有写拷贝构造函数,就会走系统默认的拷贝构造函数(注意只是浅拷贝

因此,在类的设计中,如果有函数参数是 指针数据类型,需要自己编写拷贝构造函数,避免浅拷贝的一改全改的发生

浅复制:将对象数据成员的值进行简单的复制, 最好利用系统自动生成的复制构造函数,已完成浅复制

深复制:不仅将对象数据成员的值进行复制,而且对指针型数据成员生成新空间,然后复制对应的值

浅拷贝:若对象成员是指针,(复制地址把值也改变了)会一改全改,需要自己去写一个拷贝构造函数

深拷贝:对指针数据成员开空间,然后复制对应的值

四:触发拷贝构造的3种情况

1.类对象直接赋值

CLabel name = title;

2.类对象作为函数参数时(建立局部对象)

void copy(CLabel lab);  //类对象作为函数参数

3.函数返回是类对象时(建立临时对象)

CLabel copy(CLabel lab);  //函数返回是类对象

五:类对象作为函数参数

类对象作为函数参数时,值传递

执行系统默认的拷贝构造(1次)

void copy(CLabel lab);  //类对象作为函数参数
void CLabel::copy(CLabel lab)  //按值传递 对象赋值 执行系统copy构造
{
strcpy(content,lab.content);   //形参的生命周期 调用创建走拷贝构造 用完释放析构
}

缺点:空间利用频繁,开销比较大

六:类对象作为函数返回

函数返回是类对象时,类对象也是函数参数

执行系统默认的拷贝构造(2次)

CLabel copy(CLabel lab);  //函数返回是类对象
CLabel CLabel::copy(CLabel lab)//类对象作为函数参数 执行系统copy构造
{
   strcpy(content,lab.content);   
   return lab;//函数返回是类对象
}

七:拷贝构造(何时使用)

什么时候需要自己写拷贝构造:

类对象中有指针数据成员的时候才需要自己写拷贝构造!!!

八:函数传参

函数传参有三种

1.按值传递(值传参消耗的空间特别大)

2.按地址传递(形参是指针数据类型)

3.按引用传参,共享一片内存空间;系统开销小,比较推荐

九:引用传参

1.类对象引用作为函数参数

void copy(CLabel &lab);  //引用传参
void CLabel::copy(CLabel &lab) //引用传参
{
  strcpy(content,lab.content);   //共用一片内存空间
}

2.类对象引用作为函数返回

CLabel& copy(CLabel lab);  //类对象引用作为函数返回
CLabel& CLabel::copy(CLabel lab) //类对象引用作为函数返回
{
  strcpy(content,lab.content); //共用一片内存空间
  return lab;
}

引用优势:

共享对象同一片内存空间不需要创建临时变量(不走拷贝构造 析构)

系统开销小   比较推荐

类 结构体 都是数据类型 可以类比int 当作是数据类型, 类是C++才有的 ,C没有

十:转换构造函数

转换构造(存在隐式转换的风险)  需要使用 explicit关键字来解决

转换构造函数 (一个参数)

转换构造函数,要传参一个参数,像下面直接赋值的写法是错误的,转换构造函数需要使用explicit关键字使得编辑器能够提醒此写法错误

CLabel pwd =20; (转换构造函数,无参不可)   把20变成了CLabel类型(数据类型) (类)

是一个隐式转换 编译链接却没有问题,需要使用关键字explicit加以解决,如下:

声明写上 explicit CLabel(int x);  //转换构造    按上面加粗的错误写法就会在编译的时候出现报错

正确的写法应该是CLabel pwd(20); 编译链接就没有问题了,因此转换构造在函数声明时候要写上explicit关键字

十一:C++的几种构造函数

默认构造:无参

普通(带参)构造:2参及以上

转换构造:1参 关键字explicit

拷贝构造又叫复制构造

十二:深拷贝 & 浅拷贝

深拷贝自己写拷贝构造函数,为指针数据成员开空间再复制

浅拷贝系统默认的拷贝构造函数是浅拷贝,复制地址,会一改全改

十三:析构函数作用

系统分配的空间会自动回收

程序员手动开空间,就需要手动释放,如

malloc需要free

new需要delete

那么,构造后就需要析构

析构函数没有参数,因此析构函数不能被重载

;