前言

在C++中,我们对于一个项目不再局限于头文件、源文件,而是能通过类创建独属于工程的工程代码,通过对象更方便、高效的使用函数,今天本节将学习基础关于两者的基础知识。


一、类的定义

1.1 类的定义格式

类的关键字为class,Data为类的名字,{}里为类的主体,如结构体一般结束需要分号。

class Data
{
	Data(int year = 2025, int month = 12, int day = 14)
	{	
		_year = year;
		_month = month;
		_day = day;
	}
	//这里是声明,不会开辟空间
	private:
		int _year;
		int _month;
		int _day;
};

类的内部构成

类中含有:变量和函数,变量被称为类的属性或成员变量,函数被称为类的方法或成员函数,它们被统称为类的成员。
成员变量定义在哪里对函数的调用没有影响,C++编译器会自己在类内寻找

类的建议与细节

  • 建议: 在工程中即由成员变量,也会有普通变量,为了区分一般在成员变量会加上一个特殊标识,如在变量前或后加_或者m,这个C++不做要求。
  • 细节: C++中struct可以定义类,C++同时将struct升级成为了类,在其中可以定义函数,但一般还是推荐class来定义类;并且成员函数一般默认为inline,当然struct也可以使用结构体的功能,兼容C语言的struct用法。
// C++升级struct升级成了类 
// 1、类⾥⾯可以定义函数 
// 2、struct名称就可以代表类型 
// C++兼容C中struct的⽤法 
typedef struct ListNodeC
{
   struct ListNodeC* next;
   int val;
   
}LTNode;
// 不再需要typedef,ListNodeCPP就可以代表类型 
struct ListNodeCPP
{
   void Init(int x)
	{
	  next = nullptr;
	  val = x;
	}
   ListNodeCPP* next;
   int val;
};

1.2 访问限定符定义

  • 定义: C++⼀种实现封装的⽅式,⽤类将对象的属性与⽅法结合在⼀块,让对象更加完善,通过访问权限
    选择性的将其接⼝提供给外部的⽤⼾使⽤。

限定符类别

  • public: 该词修饰的成员可在类外直接访问
  • private: 该词修饰的成员不能在类外直接访问,同包的其他类不能访问
  • protectd: 该词修饰的成员不能在类外直接访问,同包的其他类可以访问

都可在类内自由访问

限定符作用范围

  • 访问权限作用域从该访问限定符出现的位置开始开始直到下⼀个访问限定符出现时为止,如果后面没有
    访问限定符,作用域就到 } 即类结束。

限定符默认

  • class定义成员一般默认为private,struct默认为public,成员中变量限制为私有,函数设为公共。

1.3 类域定义

  • 定义: 类定义了⼀个新的作⽤域,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤::作
    ⽤域操作符指明成员属于哪个类域。
  • 作用: 通过类域,当声明与定义分离时,定义时可以指向成员函数而不是全局函数

类域限定功能

  • 原因: 通过类域隔离不同类的函数声明与定义,并给出明确的类位置
  • 实现: 类域影响的是编译的查找规则,下⾯程序中Init如果不指定类域Stack,那么编译器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在那⾥,就会报错。指定类域Stack,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找。

二、实例化

2.1 实例化概念

  • 定义: 用类类型在物理内存中创建对象的过程,又称为类示例化出对象‘
  • 原因: 类只是一种抽象描述,如同图纸,其中的成员变量只是声明,并没有分配空间,实例化出对象时,才会分配空间。
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(2024, 3, 31);
d1.Print();
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}

对象大小

  • 对象内存分配: 类示例化出的对象,每个都有独立的空间,因此对象空间中肯定有成员变量。而对于成员函数呢?首先我们在编译下可以发现调用函数时,只是一段指令,并存在代码段中,对象无法存储,那非要存储也要是函数指针。但仔细想想同一个类实例化出的对象存储的函数指针是一样的,那么会存储多次造成空间浪费。
    其实不会创建空间,可以知道调用函数被汇编成汇编指令【call】,在编译链接时就已经开始处理函数,寻找函数的地址。当然只有动态多态是在运⾏时找,就需要存储函数地址,这个我们以后会讲解。
    在这里插入图片描述

  • 内存对齐规则: 这里与C语言的结构体类似

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

tips:为什么要内存对齐
本质:用空间换效率
CPU读取数据时具有步长(与数据总线有关),若char 与 int 时,内存对齐下读取一次,而不使用这个下,不仅需要读两次,还需要拼接。

#include<iostream>
using namespace std;
// 
计算⼀下
A/B/C
实例化的对象是多⼤?
 
class A
{
public:
void Print()
{
cout << _ch << endl;
}
private:
char _ch;
int _i;
};
class B
{
public:
void Print()
{
//...
}
};
class C
{};

  在计算发现没有成员变量的B类和C类创建的对象内存大小为1,那为什么还要给1个字节?这是因为没有字节怎么证明创建了对象呢,这只是纯粹的表示对象存在

练习
#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
	}
private:
	int _a;
};

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

下⾯程序编译运⾏结果是()

A、编译报错 B、运⾏崩溃 C、正常运⾏

  大家可能初遇到这个题可能会选B,觉得p空指针指向空,故Print函数并没有初始化,其实函数并非是放在类当中的而是创建在编码区,只是一行指令,需要时只会调用call指令锁定地址,从而调用函数。

#include<iostream>
using namespace std;
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}

这个题大家应该都能做对了,这里Print函数调用还需到用到_a变量,而p是空指针,变量都没有赋值,程序就会因解引用空内存而运行崩溃。


this指针

  在对象中有着这样一种现象,调用类中的成员函数时明明没有传递参数,但却还是能够成功的将数据传入函数当中,那么编译器是怎么知道是哪个对象呢?C++的隐含的指针this解决了这个问题。

  • 定义: this 指针是 C++ 面向对象的核心概念之一,本质是非静态成员函数隐含的、指向当前对象实例的常量指针(T* const this)—— 它自动传递给每个非静态成员函数,用于区分 “成员变量” 和 “局部变量 / 参数”,并明确当前操作的是哪个对象实例。
  • 实现操作: 在编译器编译后,类的成员函数默认都会在形参的第一个位置,增加一个当前类型的指针,叫做this指针,如:
void Init(Date* const this, int year, int month, int day)
  其实类的成员函数中访问成员变量,本质都是通过this指针访问的,
  如Init函数中给_year赋值,this->_year = year
  
  注意:C++规定不能在实参和形参的位置显示写的this指针,但是可以在函数体内显示
  使用this指针
void Init(int year, int month, int day)
{
// 
编译报错:
error C2106:=: 
左操作数必须为左值
 
// this = nullptr;
// this->_year = year;
_year = year;
this->_month = month;
this->_day = day;
}

那this指针储存在哪里呢?本质this是函数的隐含参数,因此this存储在栈区


C++类的面向对象特征

  • 面向对象特性: 封装、多态、继承(本节初步了解多态)

这里通过比较C++与C语言的实现Stack代码分析:

1. 值的访问与修改:

  C++中通过访问限定符对成员变量进行了限制,不能随意直接在外部更改数据,而C语言直接就可以访问所有的值,如:s.a[s.top-1]//获取栈顶元素,这样直接访问可能会造成越界访问

2. 函数与变量的声明与定义:

  C++的函数与变量都会定义在类中,大幅度减低了函数与变量命名的冲突并明确了功能的封装,而C语言只能全部定义和实现在同一个局域里。

3. 值的传递:

  C++中通过引用与this指针实现了便捷的数据传递,C语言中只有通过指针才能实现。(我相信大家一定被指针折磨过吧)

  这里仅列举了几例两者的区别,在今后的学习中将发现C++的更多闪光点。

总结

  以上就是今天要讲的内容,讲解了类和对象的基本知识,类和对象的结构庞大还需要继续努力,加油!!!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐