10 C++定义类

1 类的定义

1)类定义的变量也叫做类的实例也叫做对象,这个过程称为类的实例化。类其实是一种特殊的类型,这种类型是自己以类的形式来定义的,它其实和intfloat没有什么太大的区别,只是类是一个比较复杂的带有函数的一个类型
2)对象的内存空间:所有成员变量的大小之和,例如sizeof(CRectangle) = 8;,即两个int型变量的内存。
3)对象之间可以用=赋值,不能用==!=><>=<=进行比较(除非这些运算符经过了重载

class 类名
{
	访问范围说明符:
		成员变量1
		成员变量2
		...
		成员函数声明1
		成员函数声明2
	访问范围说明符:
		更多成员变量
		更多成员函数声明
		...
};//注意";"

在这里插入图片描述
在这里插入图片描述

//矩形类
class CRectangle{
	public:
		int w, h;//成员变量
	void Init(int w_, int h_){
		w = w_; h = h_;
	}
	int Area(){
		return w * h;
	}
	int Perimeter(){
		return 2 * (w + h);
	}
};//必须有分号
int main()
{
	int w, h;
	CRectangle r;//r是一个对象
	cin >> w >> h;
	r.Init(w, h);
	cout << r.Area() << endl << r.Perimeter();
	return 0;
}

2 访问类的成员变量和成员函数

1)用法1:对象名.成员名

CRectangle r1, r2;//r1,r2是两个对象
r1.w = 5;
r2.Init(4,3);//成员函数

2)用法2:指针->成员名

CRectangle r1, r2;
CRectangle* p1 = & r1;//& r1指r1的地址
CRectangle* p2 = & r2;
p1->w = 5;
p2->Init(3,4);

3)用法3:引用名.成员名

CRectangle r2;
CRectangle & rr = r2;//rr作为r2的别名
rr.w = 5;
rr.Init(3,4);

2.1 类的成员函数的另一种写法:成员函数体和类的定义分开写

class CRectangle
{
	public:
		int w, h;
		void Init(int w_, int h_);
		int Area();
		int Perimeter();
};

void CRectangle::Init(int w_, int h_) {
	w = w_; h = h_;
}
int CRectangle::Area(){
	return w * h;
}
int CRectangle::Perimeter(){
	return 2 * (w + h);
}

3 类成员的可访问范围

private:私有成员,只能在成员函数内被访问
public:公有成员,可以在任何地方被访问
protected:保护成员
类的定义:不写关键字的缺省为私有成员

class className
{
	private:
		私有属性和函数
	public:
		公有属性和函数
	protected:
		保护属性和函数
};

1)类的成员函数内部,可以访问当前对象的全部属性,函数;同类其他对象的全部属性,函数。
2)类的成员函数以外的地方,只能够访问该类对象的公有成员
在这里插入图片描述
在这里插入图片描述

4 内联成员函数

内联函数:减少调用开销。成员函数也可以被定义为内联的。
有两种方式:1)inline+成员函数。2)整个函数体出现在类定义内部
在这里插入图片描述

5 重载成员函数

成员函数还可以实现重载,成员函数可以带缺省参数(不写参数就叫做缺省)。函数重载指的是具有相同函数名的一类函数,包含一个或多个不同的参数,或者参数的类型不同。

#include<iostream>
using namespace std;
class Location{
	private:
		int x,y;//成员变量
	public://三个成员函数
		void init(int x = 0, int y = 0);//规定了init的缺省值为0,0
		void valueX(int val){x = val;}//函数名valueX
		int valueX(){return x;}//还是函数名valueX,因为参数不同,这两个函数就构成了重载
};
void Location::init(int X, int Y){
	x = X;
	y = Y;
}
int main(){
	Location A;
	A.init(5);//y缺省,被赋为0
	A.valueX(5);
	cout << A.valueX();//输出5
	return 0;
}

在这里插入图片描述

6 构造函数

成员函数的一种
1)构造函数的名类名相同,可以有参数不能有返回值(void也不行)
2)是用来对对象初始化,给成员变量赋值(类似于房子装修)
3)如果定义类的时候不写构造函数,则编译器生成默认的无参数构造函数(不做任何操作)
4)如果定义了构造函数,则编译器不生成默认的无参数构造函数
4)对象生成时调用构造函数
5)一个类可以有多个构造函数
为什么需要构造函数?
1)不用专门写初始化程序,构造函数可以实现初始化工作,不用担心忘了对对象进行初始化。
2)没有构造函数,可能会导致出错。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.1 复制构造函数

copy construcor(抄袭构造函数)
1)只有一个参数,意思是对同类对象的引用。不能是X :: X ( X )不能是本类的对象
2)复制构造函数的定义:X :: X ( X & )或者X :: X ( const X & ),二者选一,后者能以常量对象作为参数
3)如果没有定义构造函数,则编译器生成默认的复制构造函数(默认的复制构造函数完成复制功能)
4)如果定义了构造函数,则编译器不再生成默认的复制构造函数,自己定义的复制构造函数不一定非要做复制的工作
在这里插入图片描述
在这里插入图片描述

6.2 复制构造函数的三种应用

1)用一个对象去初始化同类的两一个对象
在这里插入图片描述

2)如果一个函数的形参类A的对象,那么这个函数在调用的时候。类A的复制构造函数将被调用,用来初始化形参
在下面的程序例子里面classA有一个复制构造函数(下图标红的),在这个复制构造函数里面,我们没有做复制的工作,只是输出了一串字符。接下来函数Func的形参是类A的对象,那么进到这个函数里面,就要生成形参,那这个形参是用什么构造函数初始化呢?就是用复制构造函数初始化的,那复制构造函数被调用是需要参数的,那这个参数是谁呢?我们就看,在main里面Func(a2),以a2作为参数。
第二种情况的总结:按以前学习的知识,形参a1与实参a2是相等的。但是在这里,形参a1与实参a2不相等,因为a1是用自己编写的复制构造函数去初始化的,自己编写的复制构造函数并没有做复制的工作。如果不写复制构造函数,那a1a2一定相等,因为编译器会自动生成默认的复制构造函数,这个复制构造函数就是做复制的工作。
在这里插入图片描述
在这里插入图片描述
3)如果函数的返回值类A的对象,则函数返回时,A的复制构造函数就会被调用,用来初始化作为返回值存在的类A的对象
第三种情况的总结:这里就出现了个奇怪的现象,按以前学的知识,按理说Func()函数的返回值就是b,因为return b。但是学了复制构造函数之后,就不能认为Func()函数的返回值与b相等,这取决于复制构造函数是如何编写的,因为函数的返回值是通过复杂构造函数初始化的,如果复制构造函数里面不执行复制工作,那么函数的返回值就不一定和b相等。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.3.类型转换构造函数

1)类型转换构造函数是一种特殊的构造函数,顾名思义,类型转换构造函数时用来实现类型的自动转换
2)特点:只有一个参数,不是复制构造函数,在类型转换构造函数时,编译系统会自动调用,生成临时的类对象临时变量
在下面程序的例子里面,语句:Complex c2 = 12;里面=是初始化,不是赋值。在这个的初始化里面调用类型转换构造函数时,不会生成一个临时的对象。
c1 = 9;是赋值语句。赋值号的两边其实是不一样的类型。编译器自动调用了类型转换构造函数,生成了临时的Complex对象,将9作为实参,调用Complex类型转换构造函数来初始化。

在这里插入图片描述

7.析构函数

析构函数就是对象消亡时做善后工作,释放分配的空间。
成员函数的一种
1)类名相同
2)在前面加~
3)没有参数和返回值
4)一个类只有一个析构函数
5)如果没有定义析构函数,则编译器生成缺省的析构函数(不涉及释放内存功能)
6)定义析构函数,则编译器不再生成缺省的析构函数
在这里插入图片描述

7.1 析构函数与数组

当数组的生命周期结束的时候,数组的每个元素都会调用析构函数。
在这里插入图片描述

7.2 析构函数与delete

使用delete可以调用析构函数。
在这里插入图片描述

7.3 例子

在这里插入图片描述
在这里插入图片描述

8. 静态成员变量/静态成员函数

在定义成员时,前面加static关键字,就是静态成员变量/静态成员函数
在这里插入图片描述
普通成员变量与静态成员变量的本质差别,有两个
1)普通成员变量每个变量有各自的一份,而静态成员变量就一份,被共享。举例说明:修改了对象a1wa2w不受影响,但是修改了a1nTotalAreaa2nTotalArea也受影响。
2)sizeof()运算符不算静态成员变量的内存。静态成员变量不是都放在对象内部的,它放在所有对象的外面,被所有对象共享。
在这里插入图片描述
普通成员函数与静态成员函数的本质差别,
1)普通成员函数必须具体作用于一个对象(比如对象名.成员名指针->成员名引用名.成员名),而静态成员函数并不具体作用于一个对象。
2)静态成员不需要对象就能访问

8.1 如何访问静态成员

1)类名 :: 成员名CRectangle :: PrintTotal();,就调用了静态成员函数PrintTotal()
2)对象名.成员名CRectangle r; r.PrintTotal();,不意味着PrintTotal()作用于对象r,只是一种形式。
3)指针->成员名CRectangle* p; p->PrintTotal();,不意味着PrintTotal()作用于p指向的对象。
4)引用名.成员名CRectangle & ref = r; int n = ref.nTotalNumber();nTotalNumber是静态成员变量,不能说nTotalNumberref所引用的对象r的变量,它是对所有对象所共享。

静态成员变量本质是全局变量,静态成员函数本质是全局函数。
静态成员变量需要提前声明:如下图红色程序处
在这里插入图片描述
在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。
PrintTotal()是静态成员函数,想把非静态成员变量w输出,是不可以的,因为静态成员函数不作用在某一对象上面,找不到w属于谁的。非静态成员函数里面有可能调用非静态成员变量。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9.成员对象和封闭类

成员对象:一个类的成员变量是另一个类的对象,不再是常规类型的变量。
包含成员对象的类叫做封闭类(Enclosing)
在下面的程序例子里面CTyre(int r, int w) : radius(r), width(w) {}是一个构造函数,用来初始化。这里使用了一种新的方式,叫做初始化列表,不再简单地用radius = r , width = w这样的赋值语句进行初始化,把初始化的值放在小括号里面。
在这里插入图片描述
上面两个类构成了下面一个类。
如果CCar类不定义1构造函数,则CCar类编译出错,因为不知道怎么car.tyre成员对象该如何初始化,所以在生成封闭类对象,要非常明确对象中的成员对象
在这里插入图片描述

9.1 封闭类构造函数的初始化列表

初始化列表:类名 :: 构造函数(参数表) : 成员变量1(参数表), 成员变量2(参数表), ... { ... }
1)在生成封闭类对象时,先执行所有成员对象的构造函数,再执行封闭类的构造函数。
2)如果封闭类里面包含多个成员对象,这几个成员对象里面的调用顺序和成员对象在类中的说明顺序一致。
3)在消亡封闭类对象时,先执行所有封闭类的析构函数,再执行成员对象的析构函数。先构造的后析构。
下面程序中的例子:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值