类和对象(一)


1.类的引入

c语言的结构体只能定义变量,c++中结构体不仅可以定义变量,而且可以定义函数,对于结构体的定义,c++中更喜欢用class来表示

类是一个整体,在上面定义的函数要用到下面的变量时也可以搜索到

2.类的定义

class className
{
	//类体:由成员函数和成员变量
};//和结构体一样必须要有;来结束

class是类的关键字,className是你给类起的名字,{}里是类体,类体包括成员函数和成员变量,还有最后必须要加的;

class time
{
	int _year;//一般定义成员变量时候为了区分成员函数里的形参,在成员变量前加_或者
	//自己的一些区分符号
	int _month;
	int _day;
	void InitTime(int year, int month, int day)
	{
		_year = year;//如果这里我成员变量不是_year而是year的话就会与函数里的
		//形参冲突,进而无法区分是函数形参还是成员变量
		_month = month;
		_day = day;
	}
};

3.类的访问限定符及封装

3.1访问限定符

访问限定符包括public,protected,private
此处我们先认为protected和private是一样,后续会讲两者区别
访问限定符说明:
举个栗子:

class stu
{
public:
	void stuInit(int weight, int height, int num)
	{
		_weight = weight;
		_height = height;
		_num = num;
	}
	int tem;
private:
	int _weight;
	int _height;
	int _num;
};

int main()
{
	stu a;
	a.stuInit(70, 187, 1);
	return 0;
}

1.public修饰的成员在类外可以直接被访问
上述代码里的public范围里的函数stuInit()可直接访问
2.protected和private修饰的则无法被外界访问
上述代码里stu里的_weight就无法被访问
3.访问权限作用域从这一个访问限定符出现的位置到下一次访问限定符出现的位置,该范围就是这一个访问限定符作用范围,而当访问限定符直到类结束再没见访问限定符时,那么该访问限定符作用的区域就是从该访问限定符的位置到类的结束
4.class的默认权限是private,struct的默认权限是public,结构体的默认权限是为了吻合c语言里的语法,毕竟c语言里的struct都可以被访问

面试题

c++中struct和class的区别是什么
答:c++兼容c语言,struct可以作结构体和类.和class定义的类是一样的,但是默认权限不同,struct默认public,class默认private
在继承和模板参数列表,struct和class也有区别,后续介绍

3.2封装

这里就引定义吧
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来
和对象进行交互

4. 类的作用域

类定义了一个新的作用域,类的所有成员都在作用域里.在类的外部定义类的成员时,要用::来表明成员属于哪一个类,上文代码有写过类stu里stuInit函数调用有用过,可以扒前文看一下

5.类的实例化

用类类型创建对象的过程,叫作类的实例化
这里还是拿刚才的代码举栗子:

class stu
{
public:
	void stuInit(int weight, int height, int num)
	{
		_weight = weight;
		_height = height;
		_num = num;
	}
	int tem;
private:
	int _weight;
	int _height;
	int _num;
};

int main()
{
	stu a;
	a.stuInit(70, 187, 1);
	return 0;
}

这里要再次说明一下声明和定义,类的成员变量里这一部分代码:

	int _weight;
	int _height;
	int _num;

这一部分就是声明,而非定义,类的成员变量声明不要空间的

int main()
{
	stu a;//这里就是申请空间
	a.stuInit(70, 187, 1);
	return 0;
}

我们可以把类想成模型图,类的实例化就是把构建模型,把模型图实体化,类的实例化要想像模型图那样实体化就是申请空间.成员变量定义要空间.

	void stuInit(int weight, int height, int num)//这里是对函数的定义
	{
		_weight = weight;
		_height = height;
		_num = num;
	}

刚才所指的实例化说的是成员变量,但是这里的成员函数是定义,定义有没有实例化函数呢?
我们这里可以看一下上面定义的stu a的大小

int main()
{
	stu a;
	a.stuInit(70, 187, 1);
	cout << sizeof(a) << endl;//这里a的大小是16字节,这里提一点就是类的大小计算方式还是和结构体一样,
	//都是内存对齐
	return 0;
}

这里为什么没有函数的大小呢?
其实如果要把成员函数算入类的大小,可以就传一个指针,但为什么是16字节呢?

class stu
{
public:
	void stuInit(int weight, int height, int num)
	{
		_weight = weight;
		_height = height;
		_num = num;
	}
	int tem;
	//此处把访问限定符删掉了
	int _weight;
	int _height;
	int _num;
};

int main()
{
	stu a;
	stu b;
	a.stuInit(70, 187, 1);
	a._height++;

	b.stuInit(66, 177, 2);
	b._height++;

	return 0;
}

上述代码定义类stu a和stu b,a和b是两个独立的空间,里面的成员变量也是不同的,但成员函数确实同一个,都是调用的同一个函数,如果每个类函数用指针占用空间的话,就会浪费空间,但是你每次申请的类里都有一个函数,而明明他们都是同一个函数,可以理解为你家有卧室,他家也有卧室,但是像篮球场这种就是公有的,不需要每家都有篮球场
但是成员函数在哪个公共区域呢?那就是代码段,后面会介绍
所以函数的大小就是成员变量的大小,函数就存储在了代码段里面

6.类对象模型

6.1计算类对象大小

类中有成员函数和成员变量,成员函数存放于代码段中,成员变量代表类的大小

6.2 类对象的存储方式

class tem
{
	void aa();
};

int main()
{
	tem a1;
	tem a2;
	cout << &a1 << endl;
	cout << &a2 << endl;

	cout << sizeof(a1) << endl;

	return 0;
}

我前面提过,类类型创建对象是类的实例化,我还说过类的实例化是要空间,可是大家可以看到上面的代码,如果类tem的大小是0的话,那么我们就无法对a1和a2进行实例化,无法开空间,无法开地址,可是当我们打印a1,a2的地址时会发现被赋予了地址,并且大小为1字节,这里的1字节并不是用来存储数据的,因为他也没有数据可以存储,而是用来占位,表示对象存在,标识这个对象存在过,也就是a1,a2被实例化定义出来了
所以没有成员变量的类就是1字节

7.this指针

7.1指针的引出

class stu
{
public:
	void stuInit(int weight, int height, int num)//定义
	{
		_weight = weight;
		_height = height;
		_num = num;
	}
	int _weight;//声明
	int _height;
	int _num;
};
int main()
{
	stu a;
	stu b;
	a.stuInit(70, 187, 1);
	a._height++;

	b.stuInit(66, 177, 2);
	b._height++;

	return 0;
}

大家单看stuInit函数

	void stuInit(int weight, int height, int num)//定义
	{
		_weight = weight;
		_height = height;
		_num = num;
	}

这里的_weight是哪里的.是类里的吗,我们前文说过类里的成员变量是定义而非声明,定义是不要空间的,所以显然不是,那就是我们的对象stu a和stu b里的_weight,既然是stu a和stu b里实例化的_weight,我们还说过为了不浪费空间,成员函数是存放在代码段里的,每个实例化的类都是调用的同一个成员函数,所以当我们将a.stuInit和b.stuInit传参的时候,成员函数该如何区分a.stuInit传入的参数是stu a的而不是stu b的呢?
成员函数为了区分传入参数的来源,这里就引入了this指针来指向类对象,但是this指针对于用户是隐藏的,不需要用户传递,这里是靠编译器自己完成

void stuInit(int weight, int height, int num)//定义
	{
		cout<<this<<endl;//在成员函数中可以用this指针,打印会打印相应对象的地址
		this->_weight = weight;
		this->_height = height;
		this->_num = num;
	} 

在成员函数的形参和实参中不能出现this指针

7.2 this指针的特性

1. this存放在哪里?

前面提过,成员函数不在类中存放,那么this指针也不会在类中存放,也不会是代码段,代码段是成员函数执行过程指令存放的地方,那里不会存放变量.this指针是存放在栈中,this是隐含形参,在函数调用的时候,压入参数,建立栈帧,栈帧的建立就在栈里

2. this指针可以为空吗?



class stu
{
public:
	void stuInit(int weight, int height, int num)//定义
	{
		_weight = weight;
		_height = height;
		_num = num;
	}
	void print()
	{
		cout << "OK" << endl;
	}
	int _weight;//声明
	int _height;
	int _num;
};
int main()
{
	stu* a = nullptr;
	a->print();
	a->stuInit(1, 1, 1);
	a->_height = 0;
	(*a).print();
	return 0;
}
int main()
{
	stu* a = nullptr;
	a->print();//这里把a作空指针,可是并没有运行崩溃
	return 0;
}

前文提过,成员函数的指令在代码段,对于成员函数来说,他就是判断this指针,我们这里虽然传了空指针,但是我们并没有解引用空指针,没用空指针干坏事,所以并不会运行崩溃

int main()
{
	stu* a = nullptr;
	a->print();
	a->stuInit(1, 1, 1);
	return 0;
}

仔细看stuInit函数

	void stuInit(int weight, int height, int num)//定义
	{
		this->_weight = weight;//把this指针补充完整看一下
		this->_height = height;
		this->_num = num;
	}

this指针被解引用,所以这次会运行崩溃

int main()
{
	stu* a = nullptr;
	a->print();
	a->stuInit(1, 1, 1);
	a->_height = 0;
	return 0;
}

a->_height = 0;也会运行崩溃,因为你解引用对象a中的_height,而a->print();并没用解引用对象a,因为成员函数并不在类里.

int main()
{
	stu* a = nullptr;
	a->print();
	a->stuInit(1, 1, 1);
	a->_height = 0;
	(*a).print();
	return 0;
}

(*a).print();这里依旧正常运行,还是之前所说,虽然用了,看似解引用了对象a,但是成员函数并不在类中,而判断这里是否会发生运行崩溃依旧是看this指针是否被解引用

int main()
{
	stu* a = nullptr;
	a->print();
	a->stuInit(1, 1, 1);
	a->_height = 0;
	(*a).print();
	stu::print();//虽然知道你是哪个类,但是你没有传递this指针,编译器不知道你是谁的,所以会报错
	//不过后面会学习静态成员函数可以这样调用,现阶段所学不行
	return 0;
}
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图灵的六月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值