【C++进阶之路】类和对象(上)

一.面向对象和面向过程

  • 面向对象:是软件开发方法中的一种。是一种对现实世界理解和抽象的方法;是思考问题相对高级的方法。在面向对象时,我们会建立对象,主要在意对象的行为。
  • 面向过程:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。是一种思考问题的基础方法。

  • 我们以做一道红烧肉的实现来体现过程和对象的区别
  • 面向过程: 1.去买肉,切肉 2.起锅,烧油,调糖色,3.放入锅中,加入调料调味,4.顿好出锅
  • 面向对象:1.买好红烧肉 2.把红烧肉放在锅中,3.将红烧肉盛出
  • 因此:面向对象开发更加方便高效,面向过程较为繁琐。

面向对象的过程更加注重对象从而更加高效,但功能的实现有时候还是需要我们自己实现。

二.类的引入——结构体

  • 类定义:类 +类名+{ };
  • 在C++中,结构体被升级成了类,但C语言的用法还是可以使用的.

  • 证明:
struct student
{
    
    
	char _name[6];
	int _age;
	int _number;
	student* _p;
};
  • 这里的结构体被升级成了类,在C++里类名是可以被用作定义变量的,但C语言中不行,当然如果你想在这里用C语言的语法也是可以的。
  • 说明:变量的定义为了与函数调用变量进行区分,我们一般都在变量的前面加上 _

  • 类里面是可以定义函数的
struct student
{
    
    
	void Print()
	{
    
    
		cout << "Print" << endl;
	}
	char name[6];
	int age;
	int number;
	student* p;
};

  • 类被当做一个域叫做——类域
  • 目前我们学过的C++的域——全局域,局部域,命名空间域,类域。

三.类的定义

  • 与结构体相似:class +类名 +{} ;
  • 注意:;——分号不可省

1.类定义的两种方式

在类里面定义函数

class student
{
    
    
	void Print()
	{
    
    
		cout << "Print" << endl;
	}
	char _name[6];
	int _age;
	int _number;
	student* _p;
};

在类外定义函数——类域

void student::Print()
{
    
    
	cout << "Print" << endl;
}
class student
{
    
    
	void Print();
	char _name[6];
	int _age;
	int _number;
	student* _p;
};
  • 注意:在类外定义函数,需要在类里面声明一下这个函数,并且要在定义的时候函数名前加上类名+作用域限定符,表明这是该类的函数。

2.访问限定符

  • public :公有——类外可以直接访问
  • private:私有——类外不可以直接访问
  • protecter:与private类似——类外不可以直接被访问(在继承中会用到)

  • 注意
  • 1.C++为了兼容C语法,默认结构体类的成员为公有。但在class中,默认成员为私有,不能从类外直接进行访问
  • 2.其作用范围从其出现开始到遇到下一个访问限定符/类作用域结束为止。
  • 3 访问限定符只在编译期间有用,是对语法的检查,因此只在编译期间报错,在数据已经被加载到内存中时,这时看访问限定符其实是一样的,访问限定符更像是一个门槛,一个前提。

3.封装——面向对象的三大特性

  • 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
  • 说明:隐藏对象的属性和实现细节——是通过private进行实现的。外部接口——实现的函数。
  • 封装是为了更好的管理和保护数据,避免对数据进行破坏。因为会调用接口进行管理数据,这里的接口就是我们设置的规则。并且接口一般都是公开的

4.类的实例化

  • 定义一个类就如同定义了一个结构体,跟结构体的自定义类型相似,我们定义类时只是说明了类里面有什么内容,并不能直接使用类的内容,那与定义一个结构体变量就如同类的实例化
  • 这也间接反映出了域里面并不一定都是变量的定义,也有可能都是变量的声明。
class student
{
    
    
public:
	void Print();
private:
	char name[6];
	int age;
	int number;
	student* p;
};
int main()
{
    
    
	student A;//这样我们就完成了类的实例化。
	return 0;
}

四.类对象模型

求一个类的大小

  • 与求结构体的大小相似:

1.第一个成员在与结构体变量偏移量为0的地址处。

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

说明: 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。

3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

  • 需要我们注意的是在C语言中结构体中至少定义一个变量,而在C++中结构体被升级为类,可以为空,甚至可以在里面定义函数。
  • 那函数占不占内存呢?

实验便知:

class student1
{
    
    
public:
	void Print()
	{
    
    
		cout << "Print" << endl;
	}
private:
	int age;
};
class student2
{
    
    
public:
	void Print()
	{
    
    
		cout << "Print" << endl;
	}
};
class student3
{
    
    

};
int main()
{
    
    
	cout << sizeof(student1) << endl;
	cout << sizeof(student2) << endl;
	cout << sizeof(student3) << endl;
	return 0;
}

在这里插入图片描述

  • 通过student2与student3进行对比可说明——函数是不占用类里的空间。
  • 函数占用的空间在哪呢?——公共代码段(当执行时,调用此函数的代码执行即可)
  • 说明:不同的对象调用的函数是一样的。

在这里插入图片描述
在这里插入图片描述
说明:这样设计是为了空间的节省,不同对象调用的函数是一样的,在调用时执行函数的代码即可,这样一码多用节省了空间。

  • 通过student1与studen2进行对比可简单的说明——对齐方式与结构体的对齐方式一致。
  • student3的大小为1——说明类的大小最小为1,至于为什么不能为0,假设如果为0,则开辟空间为0,通过以前的学习可知,一个变量的开辟是必须有地址的,0空间无地址,互相矛盾,因此不能为0,这里的1充当的是占位符的作用。

五.this指针

C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参
数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该
指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

基本认识

class Date
{
    
    
public:
	void Print()//括号里面相当于(this),这里的this也是不能写出来的,也是隐式转换
	{
    
    
		cout << _year <<"-"<< _month<<"-" <<_year<< endl;
		//在函数里面可以使用this指针,上面的代码写完整就是
		cout <<this-> _year <<"-"<<this-> _month<<"-" <<this->_year<< endl;
		//this=NULL 这也是不行的,因为this指针是一个const修饰的类型,不能进行修改。
		//说明定义一个变量int * const this,const具有就近原则
		//放在this前this本身不能修改
		//放在*前说明*this不能被修改
	}
	void Init(int year = 1 ,int month =1,int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _day;
	int _month;
	int _year;
};

int main()
{
    
    
	Date A;
	A.Init();
	//A.Init(this);这样写是不对的,因为this是隐式转换的,这里的this转换为A的地址;
	//A.Init(&A);其实就转换为了指定对象的地址,怎么进行转换呢?A.这里通过A就能找到A的地址。
	A.Print();
	return 0;
}
  • 我们看这里的函数调用,为什么不用通过传类变量的地址,直接就能使用类里面的变量?
  • 说明:这里隐含了一个this指针,那this指针是什么呢?
  • 其实这里的this指针就是类实例化的地址。
  • 总结:this指针不能显示传参, 函数定义的参数处也不能显示出来函数内部可以使用访问对象的变量,但对this指针不能进行修改

代码解读

struct A
{
    
    
public:
	void PrintA()
	{
    
    
		cout << _a << endl;
		//这里_a相当于this->_a发生了对空指针解引用的情况
	}


	int _a;
};

int main()
{
    
    
	
    A* p = NULL;
	p->PrintA();
	//说明这里p->PrintA()等于(*p).PrintA()
	//但这里并没有语法错误:因为这里是调用里面函数,函数并不占用类的空间,因此可以调用类的函数
	//但是这里将p传进去了,p是一个空指针,在里面访问变量时,会产生对空指针解引用的情况,因此
	//具体的报错位置在函数里面的变量使用
}
  • 代码是否报错?具体报错在哪?
  • 报错,报错位置在函数里面。
  • 本质上对这里函数的调用相等于函数的传参,而对成员变量的使用才是解引用!
struct A
{
    
    
public:

	void Show()
	{
    
    
		cout << "Show()" << endl;
	}

	int _a;
};

int main()
{
    
    
	
    A* p = NULL;

	p->Show();
}
  • 代码是否报错?具体报错在哪?

  • 不会报错,因为函数里面没有对变量的访问。

  • 总结:this指针可以为空,但必须通过对象进行调用函数。

猜你喜欢

转载自blog.csdn.net/Shun_Hua/article/details/130339269