您现在的位置是:首页 >其他 >【C++】类和对象(一)网站首页其他

【C++】类和对象(一)

蛋超饭不要加蛋 2024-06-17 10:48:26
简介【C++】类和对象(一)

目录

?一.面向过程和面向对象的初步认识?

?二.类的引入?

?三.类的定义?

?四.类的访问限定符及封装?

4.1访问限定符

4.2class和struct的区别

4.3封装

?五.类的作用域?

?六.类的实例化?

?七.类对象模型?

 7.1怎么计算类对象的大小

7.2类对象的存储方式

?八.this指针?

8.1this指针的概念


 

 

?一.面向过程和面向对象的初步认识?

        在我们之前学习C语言的时候,可以知道C语言是面向过程的,其关注的是解决问题的过程,按照函数逐渐解决问题

        而C++是面向对象的,关注的是有什么对象以及对象之间的关系,通过对象之间的交互解决问题

        以点外卖为例:

        用C语言的角度来看,点外卖分为一下过程:用户选择商品,加入购物车,付款下单,商家接单,骑手送单等过程

        用C++语言的角度来看,点外卖可以分为用户、商家、骑手三个对象之间的信息交互,即用户发送一个信息告诉商家,商家接受信息后进行处理,并发送信息至骑手,骑手再和用户之间进行交流以顺利送单

        通过点外卖这个例子可以明显地看出来,当遇到一个问题是,C语言关注的是解决问题的过程,每个过程需要做什么,而C++关注的问题中含有哪些对象,各个对象需要做什么以及对象之间的信息交互

?二.类的引入?

        C语言中结构体里面只可以定义变量,而C++的结构体里面不仅可以定义变量,而且可以定义函数

        我们将之前的数据结构的栈的数据和函数封装在一个结构体中

typedef int DataType;
struct Stack
{
 void Init(size_t capacity)
 {
     _array = (DataType*)malloc(sizeof(DataType) * capacity);
 if (nullptr == _array)
 {
     perror("malloc申请空间失败");
     return;
 }
     _capacity = capacity;
     _size = 0;
 }
 void Push(const DataType& data)
 {
     // 扩容
     _array[_size] = data;
     ++_size;
 }
 DataType Top()
 {
     return _array[_size - 1];
 }
 void Destroy()
 {
     if (_array)
     {
     free(_array);
     _array = nullptr;
     _capacity = 0;
     _size = 0;
     }
 }
 DataType* _array;
 int _capacity;
 int _size;
};
int main()
{
 Stack s;
 s.Init(10);
 s.Push(1);
 s.Push(2);
 s.Push(3);
 cout << s.Top() << endl;
 s.Destroy();
 return 0;
}

可以看到数据和对数据的操作(即函数)封装在一起,这样指定的函数处理指定的数据,就会显得更加高效

?三.类的定义?

        语法:class 类名

        {

                类体

        };

        class为C++的关键字,类中的变量称为成员变量,类中的函数称为成员函数

        类中函数可以声明和定义都在类体中,也可以声明和定义分离

声明和定义都在类体中

class Student
{
public:
	void Set()
	{
		cin >> _name >> _age >> _score;
	}
	void Print()
	{
		cout << "姓名:" << _name << " " << "年龄:" << _age << " " << "成绩:" << _score << endl;
	}
private:
	char _name[10];
	int _age;
	int _score;
};

声明和定义分离

#include<iostream>
using namespace std;
class Student
{
public:
	void Set();
	void Print();
	
private:
	char _name[10];
	int _age;
	int _score;
};
void Student::Set()
{
	cin >> _name >> _age >> _score;
}
void Student::Print()
{
	cout << "姓名:" << _name << " " << "年龄:" << _age << " " << "成绩:" << _score << endl;
}

值得说明的一点:函数定义在类外时,需在函数名前加上域作用符,即类名::

类声明放在.h文件中,函数定义在cpp文件中

 一般建议使用类声明和函数定义分别放在.h和.cpp文件的方式

?四.类的访问限定符及封装?

4.1访问限定符

        C+=访问限定符总共有三个:public(公有),protect(保护),private(私有)

        对于public修饰的成员,在类外可以直接访问,即类似于结构体的.引用符

        对于protect和private修饰的成员,在类外不可直接访问

        访问限定符的作用域从限定符开始的位置到下一个访问限定符出现时为止,如果没有下一个限定访问符,则到类体结束

4.2class和struct的区别

1. C++需要兼容C语言,所以C++中也可以用struct来定义结构体,struct也可以用来定义类,与class定义类是一样的

2.默认权限不同,struct定义的类默认权限为public,class定义的类默认权限为private

3.在继承和模版等知识点中,也有一些不同的地方,在相应的知识点处会予以介绍

4.3封装

        封装的概念:

         将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来
和对象进行交互

        C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性地将其接口提供给外部的用户使用

        封装本质上是一种管理,让用户更加方便地使用类

        以收音机为例,收音机内部有很多复杂的电路和器件,在外面包装一个外壳并设置播放、录音等按钮,用户只需点击相应的按钮即可实现所需的功能,而不能也不必知道实现功能的细节和过程,仅仅对外提供功能接口,而隐藏功能实现细节,这便是封装

?五.类的作用域?

        定义一个类的同时也定义了一个新的作用域,作用域的名字即类的名字

        类的所有成员都在这个作用域,作用域的范围即类体的范围,在类外定义函数时需要指定作用域,否则会当成全局函数处理

        使用类的作用域同其他作用域一样,也是作用域名加域作用符,即类名::

?六.类的实例化?

        用类定义一个对象的过程,称为类实例化

        类似于结构体,用结构体定义一个结构体变量的过程称之为结构体的实例化

        类是对象的抽象,对象是类的一个具体表现,把类比作建筑图纸,而定义对象则是利用图纸来造房子

        类的声明不占用内存空间,定义出的对象占用实际的存储空间,一个类可以定义多个对象,将类和对象与C语言的结构体做一些比较可以更好地理解

        例如一个学生类,类中有姓名、年龄、成绩等信息,可以利用该类定义两个具体的学生,比如张三和李四

#include<iostream>
using namespace std;
class Student
{
public:
	void Set();
	void Print();
	
private:
	char _name[10];
	int _age;
	int _score;
};
void Student::Set()
{
	cin >> _name >> _age >> _score;
}
void Student::Print()
{
	cout << "姓名:" << _name << " " << "年龄:" << _age << " " << "成绩:" << _score << endl;
}
int main()
{
	Student zhangsan, lisi;//定义张三和李四
	return 0;
}

   

?七.类对象模型?

 7.1怎么计算类对象的大小

        在 C语言中,由于结构体中只有成员变量,故结构体的大小就是计算成员变量的大小,只不过要遵循内存对齐规则而已

        结构体内存对齐规则:

1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
        而C++中类既有成员变量又有成员函数,那么类的大小会包含函数吗?

7.2类对象的存储方式

        通过以下代码验证;

#include<iostream>
using namespace std;
// 类中既有成员变量,又有成员函数
class A1 {
public:
	void f1()
	{

	}
private:
	int _a;
};
// 类中仅有成员函数
class A2 {
public:
	void f2()
	{

	}
};
// 类中什么都没有---空类
class A3
{

};
int main()
{
	cout << sizeof(A1) << endl; 
	cout << sizeof(A2) << endl; 
	cout << sizeof(A3) << endl;
	return 0;
}
  运行结果:

         可以看到类A1只计算了成员变量的大小,而没有包含函数,类A2和类A3只有有无一个函数的区别,却大小都是1,由此可以验证,类的大小只包含成员变量,不包含成员函数

        总结:类的大小(遵守内存对齐规则)只包含成员变量,而不包含成员函数,成员函数存储在代码段中,对于空类,即没有成员变量的类,大小为1是为了标识对象的定义

?八.this指针?
 

8.1this指针的概念

        以学生类为例,当我们定义了一个张三和李四

#include<iostream>
using namespace std;
class Student
{
public:
	void Set()
	{
		cin >> _name >> _age >> _score;
	}
	void Print()
	{
		cout << "姓名:" << _name << " " << "年龄:" << _age << " " << "成绩:" << _score << endl;
	}
private:
	char _name[10];
	int _age;
	int _score;
};
int main()
{
	Student zhangsan, lisi;
	zhangsan.Set();
	lisi.Set();
	zhangsan.Print();
	lisi.Print();
	return 0;
}

        由本篇前面可知,类的成员函数存储在代码段,所有对象用的是同一个代码段

        那么对象在调用函数的时候,函数是怎么知道当前是哪个对象在调用它呢,比如调用Print函数,是访问张三的成员变量还是李四的成员变量呢

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

        8.2this指针的特性

        编译器是怎么设置this指针的呢?

        用户的代码:

class Student
{
public:
	void Set()
	{
		cin >> _name >> _age >> _score;
	}
	void Print()
	{
		cout << "姓名:" << _name << " " << "年龄:" << _age << " " << "成绩:" << _score << endl;
	}
private:
	char _name[10];
	int _age;
	int _score;
};

         编译器的优化

class Student
{
public:
	void Set(Student * this)
	{
		cin >> this->_name >> this->_age >> this->_score;
	}
	void Print(Student * this)
	{
		cout << "姓名:" << this->_name << " " << "年龄:" <<this-> _age << " " << "成绩:" << this->_score << endl;
	}
private:
	char _name[10];
	int _age;
	int _score;
};

用户的代码

zhangsan.Set();

编译器的优化

zhangsan.Set(&zhangsan);

         可以看到,编译器自动完成this指针的设置,即this是成员函数的隐形的第一个形参,在调用函数处,编译器自动将对象取地址作为实参传过去 

        this指针的特性

1.this指针的类型就是该类的指针,而且是指针常量,即不可改变指向

2.this指针仅在被调用的函数的函数体内有效

3.this作为形参存储在栈上,不存储在对象中,即对象的大小不包含该指针

4.this指针可以为空

        

        好啦,关于类和对象(一)的学习就先到这里,如果对您有所帮助,欢迎一键三连,您的支持是我创作的最大动力

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。