类和对象(上)

  1.类的定义

1.1 类的定义格式

  • 类的定义格式是class关键字+类名,以大括号{}为主体,最后结尾必须带分号。大括号里面的内容被叫做类的成员;类里面定义的变量被叫做成员变量或者属性;类中定义的函数被叫做类的方法或者成员函数。
  • 为了区分成员变量和参数,我们通常在成员变量后面或前面加上下划线(_)亦或者是m开头,这只是通常的做法,C++里面并没有规定,工作之后也看公司具体如何区别。
  • C++里面struct也可以定义类,方法和class一样。而struct也升级成了类,在里面是可以定义函数的,但是通常我们需要定义类的时候还是会用class关键字去定义。
  • 类里面的成员函数都是inline类型的。
class Data
{
public:
	//成员函数
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	//成员变量
	int _year;
	int _month;
	int _day;
};

1.2 访问修饰符

  •    C++用类将成员变量和方法封装在一起,访问修饰符的作用就是选择性地让用户去访问那些成员和调用方法。
  • 在上面我们可以看到public、private两个访问修饰符,其实还有一个是protected。这几个都可以按照字面意思去理解public就是公共的,可以供外部去访问或者调用;而private和protected就是不给外部去访问或者调用。(现阶段小编知识不够完备,不能讲清private和protected的区别,后面的博客会弄明白的。)
  • 修饰符的修饰区域是从自身开始到下一个修饰符之间的部分,如果是最后一个修饰符,则修饰的部分就是自身到类结束的 };
  • 在class关键字修饰的类里面:如果一个成员(函数或者变量)不加修饰符,则默认为private;而struct就相反,默认为public。
  • 一般成员变量都会被private或protected修饰保护起来,而成员函数则会被public修饰。
class Stack
{
	void Push(int x)
	{
		a[size++] = x;
	}
public:
	void Init(int n = 4)
	{
		a = (int*)malloc(sizeof(int) * n);
		if (a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		capacity = n;
		size = 0;
	}
private:
	int* a;
	int capacity;
	int size;
};
struct _Stack
{
	void Init(int n = 4)
	{
		a = (int*)malloc(sizeof(int) * n);
		if (a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		capacity = n;
		size = 0;
	}
private:
	int* a;
	int capacity;
	int size;
};
int main()
{
	Stack s1;
	s1.Init();
	//说明class修饰的类里面没有访问修饰符的成员默认是private
	s1.Push(4);

	_Stack s2;
	//说明struct修饰的类里面没有访问修饰符的成员默认是public
	s2.Init();
	return 0;
}

 

 1.3 类域

  • 类的定义形成了一个新的域叫类域,类的所有成员都被定义在这个域里面。如果需要在类域外面定义成员,则需要使用 :: 作用域操作符来指明成员属于哪个类域。
  • 如果我们在下面没有给Init指定类域,那么编译器就会认为这是一个全局定义的函数,而里面的capacity和size是类域里面的变量,在全局无法找到定义,会导致报错。
    class Stack
    {
    
    public:
    	void Init(int n = 4);
    	
    private:
    	int* a;
    	int capacity;
    	int size;
    };
    
    //声明和定义分开,需要指定类域
    void Stack::Init(int n)
    {
    	
    		a = (int*)malloc(sizeof(int) * n);
    		if (a == nullptr)
    		{
    			perror("malloc fail");
    			return;
    		}
    		capacity = n;
    		size = 0;
    	
    }

    2.类的实例化

 2.1 实例化的概念

  如果大家在学校学习过Java,那么这一点大家应该不难理解。

  • 用类类型在物理内存里创建对象的过程被成为实例化。
  • 类是对象的一种抽象描述,就像是一栋房子的图纸,图纸就是对房子的抽象描述,我们通过图纸可以造出许多相同的房子,这就是类。
  • 而类里面的成员变量就是对象的各种属性,里面只有声明变量,并没有申请内存,把“房子”造出来才会有内存。有了实物,也就是实例后,才会有物理意义上的内存。
    class Data
    {
    public:
    	//Init()是全缺省函数
    	void Init(int year = 2024, int month = 1, int day =1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	void Print()
    	{
    		cout << _year << "/" << _month << "/" << _day << endl;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    int main()
    {
    	//创建了两个实例
    	Data d1;
    	Data d2;
    	//分别调用类里面的两个成员函数
    	d1.Init(2024, 8, 15);
    	d1.Print();
    	
    	d2.Init();
    	d2.Print();
    	return 0;
    }

 2.2 对象的大小

  我们来分析一下对象实例,里面包括了成员变量和成员函数。对象实例化后,成员变量都会占用一定的空间,那成员函数呢。首先,函数在编译过后是一段指令,对象中没办法储存,这些指令储存在一个单独的区域。假如成员函数以函数指针的形式储存,我们创建了100个对象实例,那么我们就需要重复储存100个函数指针,这就浪费了很多空间。所以成员函数是不算在对象实例的大小。其实函数指针是不需要存储的,函数指针是⼀个地址,调⽤函数被编译成汇编指令[call地址],其实编译器在编译链接时,就要找到函数的地址,不是在运行时找,只有动态多态是在运行时找,就需要存储函数地址,这个我们以后会讲解。

  对象的具体大小和计算结构体大小的规则一样要内存对齐:

• 第⼀个成员在与结构体偏移量为0的地址处。
• 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
• 注意:对齐数=编译器默认的⼀个对齐数与该成员大小的较⼩值。
• VS中默认的对齐数为8
• 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
• 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

如有需要,可以参考本人写的结构体的文章 

那我们来小小练习一下:

class Data
{
public:
	
	void Init(int year = 2024, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

class Bread
{
public:
	void Toast()
	{
		;
	}
private:
	int price;
	int weight;
	char* name;
};
class Book
{

};
int main()
{
	Data d1;
	Bread bread;
	Book book;
	cout << "d1的大小为:" << sizeof(d1) << endl;
	cout << "bread的大小为:" << sizeof(bread) << endl;
	cout << "book的大小为:" << sizeof(book) << endl;

	return 0;
}

 这里可以看到,Book类里面什么都没有,这一个字节的大小就是来占个位,表示这个类存在。所以碰到空的类,不要说它的大小为0哦。

 3.this指针

  •   在前面的一段代码里面,我们通过Data创建了两个实例,我们都调用了Data类里面的两个函数,那么函数是如何知道是d1在调用还是d2在调用呢?这里C++就创建了this指针来实现。
  • 在编译器编译后,类的成员函数都默认在第一个参数前面加上本类类型的this指针,比如Data类里面的Init函数实际的样子是Init(Data* const this,int year,int month,int day)。
  • 类的成员函数访问成员变量,本质都是通过this指针来访问的,比如 _year = year等价于this->_year = year;
  • • C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针
    class Data
    {
    public:
    	//void Init(Data* const this,int year = 2024, int month = 1, int day =1)  不能这样写
    	void Init(int year = 2024, int month = 1, int day =1)
    	{
    		// this->_year = year;  等价  _year = year;
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	void Print()
    	{
    		cout << _year << "/" << _month << "/" << _day << endl;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };

     

 

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值