首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++】深入理解类与对象:面向对象编程的核心

【C++】深入理解类与对象:面向对象编程的核心

作者头像
Yui_
发布2024-11-19 09:56:27
发布2024-11-19 09:56:27
38500
代码可运行
举报
文章被收录于专栏:Yui编程知识Yui编程知识
运行总次数:0
代码可运行

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

C语言是面向过程的,关注的过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C++是基于面向对象的,关注的对象,将一件事情拆分成不同的对象,靠对象间的交互完成。

面向对象编程是一种常用的编程思维,它强调万物皆为对象,因此在编程时我们可以把现实世界中的事物抽象成程序中的对象,从而实现软件的设计与开发。与传统的函数编程不同,面向对象编程注重将数据与行为封装在一起,也就是对象包含数据状态,还包含可调用的行为方法。

面向对象编程的特点在于,它具有封装,继承和多态三大特性,封装意味着对象的状态和行为进行封装,使其对外只需要暴露出必要的接口,从而提高了安全性和可维护性;继承是指某个对象可以继承另一个对象的特性,从而快速构建相似属性的对象;多态是指同一种行为在不同的对象上具有不同的表现形式,即在不同的情境下,同一方法可以被不同的对象进行调用。

总之面向对象是一种强大的编程方法,它具有高度的封装性、灵活的继承性和强大的多态性,通过使用对象作为程序的基本处理单元,实现了数据和行为高度有机结合,可以使程序更加高效,结构清晰,并方便管理和扩展。

2.类的引入

C语言结构体中只能定义变量,在C++中,结构体不加可以定义变量,也可以定义函数。比如:

代码语言:javascript
代码运行次数:0
运行
复制
typedef int DataType;

struct  stack
{
	int top;
	int capacity;
	DataType* array;
	void Init() {
		top = 0;
		array = (DataType*)malloc(sizeof(DataType) * 4);
		if (array == nullptr) {
			perror("malloc");
			return;
		}
		capacity = 4;
	}
	void Push(DataType x) {
		if (top == capacity) {
			//扩容
			DataType* tmp = (DataType*)realloc(array, sizeof(DataType) * (capacity * 2));
			if (tmp == nullptr) {
				perror("realloc");
				return;
			}
			array = tmp;
			capacity *= 2;
		}
		array[top] = x;
		top += 1;
	}
	DataType Top() {
		if (top == 0) {
			cout << "stack is empty";
		}
		return array[top - 1];
	}
};

int main()
{
	struct stack ss;
	ss.Init();
	ss.Push(1);
	ss.Push(1);
	ss.Push(1);
	return 0;
}

用C语言实现的栈,结构体中只能定义变量,而现在用C++实现,会发现struct中也可以定义函数。 同时我们也发现,我们再也不用像C语言那样在传个结构体类型到函数中了,直接就可以使用结构体中的变量。 不过在C++中,我们更倾向于用class来定义类。

3.类的定义

代码语言:javascript
代码运行次数:0
运行
复制
class ClassName{
	//类的组成成分:成员变量和成员函数
};

class为定义类的关键字,ClassName为类的名字,{}中为类的主体。注意分号就是了。 类体中的内容称为类的成员:类中的变量称类的属性或成员变量;类中的函数称为类的方法或者成员函数。 类的两种定义方式

  1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
代码语言:javascript
代码运行次数:0
运行
复制
class TestClass{
public:
	void Print(){
		cout<<_a<<_b<<_c<<endl;
	}	
private:
	char* _a;
	int _b;
	int _c;
};
  1. 类声明放在.h文件中,成员函数定义在.cpp文件中,注意:成员函数名前面需要加类名::
代码语言:javascript
代码运行次数:0
运行
复制
//test.h
class TestClass(){
public:
	void Print();
private:
	char* _a;
	int _b;
	int _c;
};

//test.cpp
void TestClass::Print(){
	cout<<_a<<_b<<_c<<endl;
}

在一般情况下,更期望采用第二种方式。

类中成员变量命名建议:建议在命名前或者后加上下划线。

代码语言:javascript
代码运行次数:0
运行
复制
name  ->  _name

age  ->   _age

这样的话,可以很好的类中的成员变量和其他变量。

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

4.1 访问限定符

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

域作用限定符
域作用限定符
  1. public修饰的成员在类外可以被直接访问。
  2. protected和private修饰的成员在类外不能直接被访问。
  3. 访问权限作用域从该修饰限定字符出现的位置开始直到下一个访问限定符出现为止。
  4. 如果后面没有访问限定符,作用域就到}截止。
  5. class的默认访问权限为private,struct的默认访问权限为public。 提问:C++中的struct和class的区别 回答:C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义类默认访问权限是private。 注意:访问限定符只在编译时有效,当数据映射到内存1后,没有任何访问限定符的区别

4.2 封装

将数据和操作方法进行有机结合,隐藏对象的属性和实现细节,仅公开对外接口和对象进行交互。 封装的本质是一种管理,让用户更方便使用类。 就像诸多的电子产品,即使内部结构复杂,但对于使用者来说,不需要区关心它们的内部核心部件,只需要操作其暴露在外部的按钮就可以了,让用户与其完成交互。 在C++语言中实现封装,可以通过将数据以及操作数据的方法进行有机的结合,通过访问权限来隐藏对象内部的细节,控制那些方法可以在类外部直接被使用。

5.类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类外定义成员时,需要使用::作用域操作符来指明成员属于哪个类域。

代码语言:javascript
代码运行次数:0
运行
复制
class Person
{
public:
	void PrintPersonInfo();
private:
	char name_[20];
	char age_;
};

//指定类域
void Person::PrintPersonIn()
{
	//...
}

6.类的实例化

用类类型创建对象的过程,称为类的实例化

  1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成一个类,来描述学生的信息。
  2. 一个类可以实例化出多个对象,实例化出的对象,占用实际的物理空间,存储类成员变量。
代码语言:javascript
代码运行次数:0
运行
复制
int main()
{
	Person yui;//实例化对象
	yui.age_ = 18;//初始化
	return 0;
}

Person类是没有空间的,只要Person类实例出的对象才有具体的年龄。

7.类的空间大小

我们知道类中可能会存在成员函数和成员变量。

代码语言:javascript
代码运行次数:0
运行
复制
class test
{
	public:
	void print()
	{
		cout<<_a<<endl;
	}
	private:
	int _a;
}

当我们用类初始化对象后,对象中会存储什么呢?是成员变量和成员函数都存储,还是只存储成员变量呢? 在我们初始化对象时,每个对象的成员变量是不同的,但是函数是相同的,如果我们的对象需要存储成员函数,那相同的代码保存多次,就会造成空间的浪费。为此我们可以只存储成员变量,在把成员函数放在公共的代码区。

类实例化的空间分配
类实例化的空间分配
代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
using namespace std;
class a
{};

class b
{
private:
	int a_;
};

class c
{
public:
	void print()
	{
		cout << a_;
	}
private:
	int a_;
};
int main()
{
	cout << sizeof(a)<<endl<<sizeof(b)<<endl<<sizeof(c);
	return 0;
}

观察代码,你觉得打印结果会是什么呢?

代码语言:javascript
代码运行次数:0
运行
复制
1 4 4

可能第一个会有些失误,为什么会是1呢? 这是因为空类比较特殊,编译器会给一个字节来唯一标识这个类的对象。

7.1 内存对齐规则

代码语言:javascript
代码运行次数:0
运行
复制
结构体的第一个成员永远放在偏移量为0的位置
代码语言:javascript
代码运行次数:0
运行
复制
从第二个成员开始,往后的每个成员都要对齐到某个对齐数的整数倍处。对齐数:结构体成员自身的大小和默认对齐数的较小值。VS的默认对齐数是8。gcc没有默认对齐数,对齐数就是结构体成员的自身大小
代码语言:javascript
代码运行次数:0
运行
复制
结构体的总大小,必须是最大对对齐数的整数倍,最大对齐数是:所有成员的对齐数的最大的值
代码语言:javascript
代码运行次数:0
运行
复制
如果嵌套了结构体,嵌套的结构体对齐到自己最大对齐数的整数倍处

在我的这篇文章有关于内存对齐的较详细的讲解:深度刨析自定义类型(结构体、枚举、联合)

8. this指针

在 C++ 中,this 指针是一个隐含的指针,它指向当前对象的地址。它在类的成员函数内部可用,允许访问对象的成员变量和成员函数。以下是关于 this 指针的一些关键点:this 指针常用于区分成员变量和参数名相同的情况下

代码语言:javascript
代码运行次数:0
运行
复制
class stu
{
public:
	void Init(int age)
	{
		this->ags = age;
	}
private:
	int ags;
};

8.1 this指针的特性

  1. this指针的类型:类类型* const,因为const的存在,不能给this指针赋值。
  2. 只能在成员函数内部使用
  3. this指针本质是成员函数的形参·,当对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针。
  4. this指针是成员函数第一个隐含的指针形参,一般情况下,由编译器通过ecx寄存器自动传递,不需要用户传递。

练习1: 下列程序的运行结果:1.编译报错 2. 运行崩溃 3.正常运行

代码语言:javascript
代码运行次数:0
运行
复制
class Test1
{
public:
	void Print()
	{
		cout<<"haha"<<endl;
	}
private:
	int a_;
};

int main()
{
	Test1* p = nullptr;
	p->Print();
	return 0;
}

练习2: 下列程序的运行结果:1.编译报错 2. 运行崩溃 3.正常运行

代码语言:javascript
代码运行次数:0
运行
复制
class Test2
{
public:
	void Print()
	{
		cout<<a_<<endl;
	}
private:
	int a_;
};

int main()
{
	Test2* p = nullptr;
	p->Print();
	return 0;
}

答案: 练习1:正常运行 练习2:运行崩溃 可能有人会认为练习1中的p->Print();这里对空指针解引用了,实则不然。这里不涉及对空指针的解引用,只是一个函数调用而已,因为成员函数的地址不存在对象中,在公共代码区域。这里p为空指针传入print中,然后this接受print地址直接输入。真正的对空指针的解引用在Print函数内,this接受的是a_,因为a_是成员变量会对print的p进行解引用。

9.总结

文章首先区分了面向过程与面向对象的编程思想,接着引入了类的定义,讲解了类的成员变量、成员函数以及访问限定符,强调了封装的重要性。通过实例化,读者应了解到类如何描述对象,以及类的空间大小和内存对齐规则。最后我们深入探讨了this指针的特性及其在成员函数中的应用,结合练习题帮助读者巩固理解。后续我会补上类和对象的其他特点。期待你的关注。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.面向过程和面向对象的初步认识
  • 2.类的引入
  • 3.类的定义
  • 4. 类的访问限定符及封装
    • 4.1 访问限定符
    • 4.2 封装
  • 5.类的作用域
  • 6.类的实例化
  • 7.类的空间大小
    • 7.1 内存对齐规则
  • 8. this指针
    • 8.1 this指针的特性
  • 9.总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档