c++复习(2)拷贝构造函数与运算符重载

目录

  • 前言
  • 拷贝构造函数
    • 函数定义
    • 调用
    • 缺省(默认)的拷贝构造函数 -- 浅拷贝
    • 涉及指针或者内存操作
      • 用char *
      • 用char[]
      • 用string
    • 自己写的拷贝构造函数
      • 类中数据含有指针
      • 类中含有未初始化的指针数据
    • 使用容器
  • 运算符重载
    • 单目运算符重载
      • ++i 和 --i
      • i++ 和 i--
    • 双目运算符重载
    • 以友元函数形式重载
    • 特殊之 [] 运算符的重载
  • 题目
    • 分数的加减乘除(运算符重载)
      • 输入
      • 输出
      • 样例输入
      • 样例输出
      • 代码
    • 时钟调整(运算符前后增量)
      • 题目描述
      • 输入
      • 输出
      • 样例输入
      • 样例输出
      • 代码

前言

这几天都没怎么复习。。今天感觉不行了要挂了,赶快看两眼,做几道oj。。。

题外话:

因为下学期报了图形学,感觉就难顶,这几天都在摸鱼 ,石乐志一样疯狂写一个mc的渲染的着色器,算是当作预习一下opengl吧。。。 所以博客咕咕了。。。

c++复习(2)拷贝构造函数与运算符重载_第1张图片

目前进度只写完了辉光和阴影。。。

预告一蛤,以后有空的话,我会专门写一个教学专栏,从0到1,写一个mc的着色器,一步一步完成各种特效,什么阴影啊辉光啊屏幕空间反射啊体积云啊等等

好了扯远了。 婷芷 还是来复习拷贝构造函数与运算符重载罢(无慈悲

拷贝构造函数

拷贝构造函数是一个头疼的点,因为每当对象创建时,都需要它。我们习惯于写 int a = 123; 其实这就是一种拷贝构造函数,将值为123的int类型,赋值给变量a,本质是创建了新的变量。

因为灿哥对拷贝构造函数有一手的,所以我向他请教了一些这方面的姿势

函数定义

它的函数名与类名相同,但它只有一个参数,即同类的一个对象的引用。同时也说明了:不允许有形如 X( X r)的构造函数

Cat( Cat& c ); 
   //或
Cat( const  Cat& c ); // 一般用这种,防止有人偷偷摸摸改c的数据

调用

创建新对象时,当使用一个已知的对象来初始化另一个对象时,系统会自动隐式地调用拷贝构造函数。

Cat cat2(cat1);  //调用拷贝构造函数

缺省(默认)的拷贝构造函数 – 浅拷贝

如果你不定义拷贝构造函数,那么使用 Cat cat2(cat1) 时,可以简单理解为:把cat1的简单数据原封不动地复制到cat2里,但不做其他操作。

Cat (int age_) {age=age_;}

Cat cat1(5);  		//小猫1的年龄是5
Cat cat2(cat1);  	//调用拷贝构造函数,小猫2的年龄也是5

涉及指针或者内存操作

很麻烦,乖乖自己写拷贝构造函数

Cat cat1("伞兵1号");  	//小猫1的名字是伞兵1号
Cat cat2(cat1);  		//调用拷贝构造函数,小猫2的名字也是伞兵1号

有3种情况

用char *

class Cat {
		char *name;
	public:
		CPerson() {}
		Cat(char *name_) {
			name=new char[strlen(n)+1];
			strcpy(name,n);
		}
};

如果通过某种方式改变cat1的name,那么cat2的name也随之改变

用char[]

class Cat {
		char name[10];
	public:
		CPerson() {}
		Cat(char *name_) {
			strcpy(name,n);
		}
};

如果通过某种方式改变cat1的name,那么cat2的name不会改变

用string

同用char[]

都是关于地址的知识,不用多说了8

自己写的拷贝构造函数

如果你写的拷贝构造函数涉及内存操作或者数组,书本上称之为深拷贝。

类中数据含有指针

若类中的指针数据是在定义时就分配空间的,则在构造方法中也不需要分配空间

#include 
#include
#include
using namespace std;

class CPerson {
		//在构造函数前就分配空间
		char *name=new char[10],*address=new char[10];
	public:
		CPerson();
		CPerson(char *n,char *a) {
			strcpy(name,n);
			strcpy(address,a);
		}
		CPerson(CPerson &r_p) {
			//这种写法不用分配空间
			//name=new char[20];
			//address=new char[20];
			strcpy(name,r_p.name);
			strcpy(address,r_p.address);
		}
		void print() {
			cout<<name<<","<<address<<endl;
		}
		void setName(char* n) {
			strcpy(name,n);
		}

};
int main() {
	CPerson c1("zhangsan","shenda");
	c1.print();
	CPerson c2(c1);
	c2.print();
	c2.setName("lisi");
	cout<<endl;
	c1.print();
	c2.print();
}

类中含有未初始化的指针数据

若类中的指针数据,是在构造方法中分配空间的,则拷贝构造函数中也要分配空间

#include 
#include
#include
using namespace std;

class CPerson {
		char *name,*address;
	public:
		CPerson() {
			//在构造函数里才分配空间
			name=new char[10];
			address=new char[10];
		}
		CPerson(char *n,char *a) {
			name=new char[10];
			address=new char[10];
			strcpy(name,n);
			strcpy(address,a);
		}
		CPerson(CPerson &r_p) {
			//再写一遍,因为新的对象是在构造函数里进行内存分配 的
			name=new char[20];
			address=new char[20];
			strcpy(name,r_p.name);
			strcpy(address,r_p.address);
		}
		void print() {
			cout<<name<<","<<address<<endl;
		}
		void setName(char* n) {
			strcpy(name,n);
		}

};

int main() {
	CPerson c1("zhangsan","shenda");
	c1.print();
	CPerson c2(c1);
	c2.print();
	c2.setName("lisi");
	cout<<endl;
	c1.print();
	c2.print();
}

使用容器

若用string类型来处理字符串,则是安全的(当成int那种类型就行),不需要提供拷贝构造函数

这种方法万事大吉,无脑操作就行。懒狗(指zwc 我自己 )最爱

值得注意的是,使用vecrot,deque等线性容器,也可以类似string一样不考虑内存的分配,而且线性容器可以存储任何数据类型,直接拷贝即可。

运算符重载

关于运算符重载,当时再第一遍学习的时候,有更详细的理解:【c++运算符重载简单讲解】

在很多时候,我们希望通过运算符来表达类之间的运算关系,比如有分数类 Fraction,我们希望

Fraction f1, f2, f3;
f3 = f1 + f2;

而不是

f3 = add(f1, f2);

显然两者应该得到相同的运算结果,而前者的代码可读性更好。于是有人一拍脑袋,想:

f3 = f1 + f2,左操作数是 f1,那么和 add(f1, f2) 一样,如果把 + 运算符当作函数,那 + 运算符等同于 add函数(其实本质上就是把运算符看作函数调用即可)。

单目运算符重载

在一个对象的运算符重载中,默认左值就是该对象本身。而单目运算符不需要右操作数。

++i 和 --i

重载前增运算符,因为是单目运算符,因为不需要操作数,参数列表填空即可。

class Cat
{
	int age;
public:
	Cat operator ++ ()
	{
		age++;
	}
};

i++ 和 i–

虽说单目运算符不需要右操作数,但是为了和上面的前自增(++i)区分,还是在参数列表里面加上一个int。除此之外,因为后++操作是在加之前就返回结果,所以我们需要保存操作之前的结果。8 door ,康 code ⑨ dong 了。

class Cat
{
	int age;
public:
	Cat operator ++ (int)
	{
		Cat temp = (*this);	// 先复制自己
		age++;				// 操作
		return temp;		// 返回增加前的自己
	}
};

双目运算符重载

双目运算符,默认形参为右操作数,左操作数为对象本身。

class Day
{
	int date;
public:
	Day(){}
	Day(int d){date=d;}
	Day operator + (Day& d2)
	{
		return Day(d2.date+date);
	}
};

注:右操作数,即形参,可以是任何类型。

以友元函数形式重载

一般都是在需要cout自定义类型的时候用的。友元函数运算符重载的最大特点就是:当前类要作为右操作数时,使用友元函数运算符重载

以下例子演示了重载cout以输出自定义类型:

class People
{
private:
	string name;
	int age;
public:
	People(){}
	People(string na, int ag){name=na;age=ag;}	
	friend ostream& operator << (ostream& os, const People& p)
	{
		os<<"我的名字是:"<<p.name<<" 我的年龄是:"<<p.age;
		return os;
	}
};

int main()
{
	People p("下北泽医科大学李田所", 24);
	cout<<p<<endl;

	return 0;
}

c++复习(2)拷贝构造函数与运算符重载_第2张图片

特殊之 [] 运算符的重载

不细了,自己品

class test
{
	vector<int> v{1,2,3,4,5};
public:
	int& operator [] (int index)
	{
		return v[index];
	}
};

int main()
{
	test t;
	cout<<t[1]<<endl;

	return 0;
}

c++复习(2)拷贝构造函数与运算符重载_第3张图片

题目

分数的加减乘除(运算符重载)

Fraction类的基本形式如下:

c++复习(2)拷贝构造函数与运算符重载_第4张图片

要求如下:

1.实现Fraction类;common_divisor()和contracted()函数体可为空,不实现具体功能。

2.编写main函数,初始化两个Fraction对象的,计算它们之间的加减乘除。

输入

第1行:依次输入第1个和第2个Fraction对象的分子和分母值。

输出

每行依次分别输出加减乘除计算后的Fraction对象(直接输出分数值,不需要约简)。

样例输入

1 3 2 5

样例输出

fraction=11/15
fraction=-1/15
fraction=2/15
fraction=5/6

代码

#include 
#include 
#include 
#include 
#include  
#include 
#include 
#include 

using namespace std;

class Fraction
{
private:
	int fz, fm;
public:
	Fraction(){fz=1; fm=1;}
	Fraction(int z, int m){fz=z; fm=m;}
	friend ostream& operator << (ostream& os, const Fraction& f)
	{
		os<<f.fz<<"/"<<f.fm;
		return os;
	}
	Fraction operator + (const Fraction f2)
	{
		int newfm = fm*f2.fm;
		int newfz = fz*f2.fm + f2.fz*fm;
		return Fraction(newfz, newfm);
	}
	Fraction operator - (const Fraction f2)
	{
		Fraction f2_neg = Fraction(-f2.fz, f2.fm);
		return (*this) + f2_neg;
	}
	Fraction operator * (const Fraction f2)
	{
		return Fraction(fz*f2.fz, fm*f2.fm);
	}
	Fraction operator / (const Fraction f2)
	{
		Fraction f2_rev = Fraction(f2.fm, f2.fz);
		return (*this) * f2_rev;
	}
};

int main()
{
	int fz1, fm1, fz2, fm2;
	cin>>fz1>>fm1>>fz2>>fm2;
	Fraction f1(fz1, fm1), f2(fz2, fm2);
	cout<<"fraction="<<(f1+f2)<<endl;
	cout<<"fraction="<<(f1-f2)<<endl;
	cout<<"fraction="<<(f1*f2)<<endl;
	cout<<"fraction="<<(f1/f2)<<endl;

	return 0;
}

时钟调整(运算符前后增量)

题目描述

假定一个时钟包含时、分、秒三个属性,取值范围分别为011,059,0~59,具体要求如下:

1、用一元运算符++,并且是前增量的方法,实现时钟的调快操作。例如要把时钟调快5秒,则执行5次” ++<对象> “ 的操作

2、用一元运算符–,并且是后增量的方法,实现时钟的调慢操作。例如要把时钟调慢10秒,则执行10次” <对象>-- “的操作

3、用构造函数的方法实现时钟对象的初始化,用输出函数实现时钟信息的输出

clock是系统内部函数,所以不要用来做类名或者其他

输入

第一行输入时钟的当前时间时、分、秒

第二行输入t表示有t个示例

第三行输入t个整数x,如果x为正整数,则表示执行调快操作,使用重载运算符++;如果x为负整数,则表示执行调慢操作,使用重载运算符–

每次的调快或调慢操作都是承接上一次调整后的结果进行,例如先调快10秒,再调慢2秒,那么调慢2秒是接着调快10秒后的结果进行的

输出

每行输出每个时钟调整操作后的时分秒

样例输入

11 58 46
4
5 70 -22 -55

样例输出

11:58:51
0:0:1
11:59:39
11:58:44

代码

#include 
#include 
#include 
#include 
#include  
#include 
#include 
#include 

using namespace std;

class Clock
{
private:
	int h, m, s;
public:
	Clock(){}
	Clock(int H, int M, int S){h=H;m=M;s=S;}
	friend ostream& operator << (ostream& os, Clock c)
	{
		os<<c.h<<":"<<c.m<<":"<<c.s;
	}
	void add()
	{
		s++;
		if(s==60) s=0, m++;
		if(m==60) m=0, h++;
		if(h==12) h=0;
	}
	void sub()
	{
		s--;
		if(s==-1) s=59, m--;
		if(m==-1) m=59, h--;
		if(h==-1) h=11;
	}
	//  x = ++Clock
	Clock operator ++ ()
	{
		add();
		return (*this);	
	}
	//  x = Clock++
	Clock operator ++ (int)
	{
		Clock cp = (*this);
		add();
		return cp;	
	}
	//  x = --Clock
	Clock operator -- ()
	{
		Clock cp = (*this);
		sub();
		return cp;	
	}
	//  x = Clock--
	Clock operator -- (int)
	{
		Clock cp = (*this);
		sub();
		return cp;	
	}
};

int main()
{
	int h,m,s,t,v;
	cin>>h>>m>>s;
	Clock c(h,m,s);
	cin>>t;
	while(t--)
	{
		cin>>v;
		if(v>0)
		{
			while(v--) c++;
		} else
		{
			v = -v;
			while(v--) c--;
		}
		cout<<c<<endl;
	}

	return 0;
}

你可能感兴趣的:(c++期末复习,c++,构造函数)