一、面向过程和面向对象的认识

C语言是一种过程式的、结构化的通用编程语言。它的设计强调简洁、高效和对硬件的底层访问。C语言的核心是函数和过程,代码围绕着要执行的操作来组织。
C++是一门面向对象的编程语言, 在 C 语言基础上发展而来的多范式编程语言。它支持过程式编程、面向对象编程和泛型编程。C++的核心思想是“一切皆为对象”,并增加了对类、继承、多态等特性的支持,同时完全兼容 C 的语法。

二、类和对象的概念

在C语言中,程序一般由数据和算法组成,数据和算法彼此独立,关联性不强;
但是在C++中将相互关联数据和算法封装起来,形成了结构体或类,它们都是一个抽象的概念

只有定义类的变量时,数据才会真实存在,这个变量我们称之为对象

三、结构体的升级

在C语言中结构体(struct)只能定义变量,C++对其进行了全面升级,在C++中不仅能定义变量还可以定义函数。

struct Stack
{
	void Init()
	{
		array = (int*)malloc(sizeof(int) * 4);
		if (array == nullptr)
		{
			perror("malloc fail!");
			return;
		}
		top = 0;
		capacity = 4;
	}
	int* array;
	size_t top;
	size_t capacity;
};

四、类的定义

在C++中也有类似于结构体(struct)的类型叫类,class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面的分号不能省略。类体中的内容称为类的成员;类中的变量称为类的属性或成员变量;类中的函数称为类的方法或者成员函数。

class Stack
{
public:
	//成员函数
	void Init(int n = 4)
	{
		array = (int*)malloc(sizeof(int) * n);
		if (array == nullptr)
		{
			perror("malloc fail!");
			return;
		}
		top = 0;
		capacity = n;
	}
private:
	//成员变量
	int* array;
	size_t top;
	size_t capacity;
}; //分号不能省略

1、声明和定义不分离

声明和定义全部放在类中,在类体中定义成员函数,这种方式编译器可能会将成员函数默认为内联函数(inline)。

//Stack.h
class Stack
{
public:
	//成员函数
	void Init(int n = 4)
	{
		array = (int*)malloc(sizeof(int) * n);
		if (array == nullptr)
		{
			perror("malloc fail!");
			return;
		}
		top = 0;
		capacity = n;
	}
private:
	//成员变量
	int* array;
	size_t top;
	size_t capacity;
};

2、声明和定义分离

1、声明放在.h文件,定义放在.cpp文件中
2、类是一个新的作用域(类域),::是域作用的访问限定符,在类外定义的成员函数需要使用类域名加::加成员函数名来指明函数属于哪个类域才能在类外面定义成员函数。

//Stack.h
class Stack
{
public:
	//成员函数
	void Init();
private:
	//成员变量
	int* array;
	size_t top;
	size_t capacity;
}; 

//Stack.cpp
#include"Stack.h"

void Stack::Init()
{
    array = (int*)malloc(sizeof(int) * 4);
	if (array == nullptr)
	{
		perror("malloc fail!");
		return;
	}
	top = 0;
	capacity = 4;
}

3、成员变量命名风格

为了区分成员变量,一般习惯上成员变量会加一个特殊标识,如_或者m开头。

class Date
{
public:
	void Init(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

五、类的访问限定符

  • C++实现封装的方式,用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限的选择性将其接口提供给外部用户使用。
  • 访问权限作用域从该访问限定符出现的位置开始到下一个访问限定符出现为止,如果后面没有访问限定符,作用域就到 } 即类结束。
  • C++中,class和struct的区别:class定义成员没有被访问限定符修饰时默认为private,struct默认为public。

六、类域

类定义了⼀个新的作用域,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
类域影响的是编译的查找规则,下⾯程序中Init如果不指定类域Stack,那么编译器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪⾥,就会报错。指定类域Stack,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。

//Stack.h
class Stack
{
public:
	//成员函数
	void Init(int n = 4);
private:
	//成员变量
	int* array;
	size_t top;
	size_t capacity;
}; 

//Stack.cpp(声明和定义分离)
#include"Stack.h"

void Stack::Init(int n)
{
    array = (int*)malloc(sizeof(int) * n);
	if (array == nullptr)
	{
		perror("malloc fail!");
		return;
	}
	top = 0;
	capacity = n;
}


int main()
{
    Stack st;
    st.Init();
    return 0;
}

类域:解决的是类与类之间命名冲突的问题。

七、类的实例化

1、实例化概念

1、用类类型在物理内存中创建对象的过程,称为类实例化出对象。
2、打个比方:类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,设计图规划了有多
少个房间,房间大小功能等,但是并没有实体的建筑存在,也不能住人,用设计图修建出房子,房子才能住人。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据。

在这里插入图片描述

Date类的实例化代码:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	//这里只是声明,没有开空间
	int _year;
	int _month;
	int _day;
};

int main()
{
	//Date类实例化出对象d1,d2
	Date d1;
	Date d2;
	d1.Init(2025, 6, 24);
	d1.Print();
	d2.Init(2023, 4, 20);
	d2.Print();
	return 0;
}

运行结果如下:

在这里插入图片描述

2、对象大小

1、实例化出的对象中只存储成员变量

2、实例化出的对象中不存储成员函数,首先函数被编译后是⼀段指令,对象中没办法存储,这些指令存储在⼀个单独的区域(代码段),Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year/_month/_day存储各自的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象中就浪费了。如果用Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。(另外:函数指针是不需要存储的,函数指针是⼀个地址,调用函数被编译成汇编指令[call 地址],其实编译器在编译链接时,就要找到函数的地址,不是在运行时找)

3、对于有一个到多个成员函数的类对象的内存对齐计算规则同结构体内存对齐的计算规则。

4、对于一个成员变量都没有的类,编译器默认分配一字节的空间,因为如果⼀个字节都不给,怎么表示对象存在过呢!所以这里给1字节,纯粹是为了占位标识对象存在。

class A
{
public:
	void Print()
	{
		cout << _ch << endl;
	}
private:
	char _ch;
	int _i;
};

class B
{
public:
	void Print()
	{
		//...
	}
};

class C
{};

int main()
{
	A a;
	B b;
	C c;
	cout << sizeof(a) << endl;
	cout << sizeof(b) << endl;
	cout << sizeof(c) << endl;
	return 0;
}

在这里插入图片描述

八、this指针

下面我用上面Date类的实例化代码来进行this指针的讲解。
1、Date类中有Init与Print两个成员函数,函数体中没有关于不同对象的区分,那当d1调用Init和Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?C++给了一个隐含的this指针解决这个问题。

2、编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this指针。比如Date类的Init的真实原型为: void Init(Date* const this, int year, int month, int day)。类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值,this->_year = year;

3、C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示和使用this指针。

在这里插入图片描述

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐