【C++】类和对象1
2026/7/5 15:32:12 网站建设 项目流程

类和对象1

1、类的定义

C++中可以用struct定义一个类,但更多的是使用class定义类

举例:

C语言中的结构体

// C 语言:仅能定义变量,不能定义函数structStudent{charname[20];intage;// 错误!C 不允许结构体里写函数// void show() { ... }};

C++中的类

// C++ 类:变量 + 函数 + 访问控制class Student{private:// 成员变量(属性)std::string name;intage;public:// 成员函数(方法)voidsetInfo(std::string n,inta){name=n;age=a;}voidshowInfo(){std::cout<<name<<" "<<age<<std::endl;}};
  • C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化是struct中可以定义函数,一般情况下我们还是推荐用class定义类。

类的两种定义方法

  • 声明和定义全部放在类体中
//定义一个栈的结构体class Stack{public://初始化栈voidInit(intn=4){array=(int*)malloc(sizeof(int)*n);if(nullptr==array){perror("malloc申请空间失败");return;}capacity=n;top=0;}int*array;size_tcapacity;size_ttop;};

注意成员函数如果在类中定义,编译器可能会将其当成内联函数处理,最终是否内联由编译器决定

  • 类的声明放在.h文件中,成员函数的定义放在.cpp文件中
Stack.h文件 class Stack{public://成员函数//定义在类⾯的成员函数默认为inlinevoidInit(intn=4);//成员变量int*array;size_tcapacity;size_ttop;};
Stack.cpp文件#include"F.h"voidStack::Init(intn)//这里不能写int n=4;因为默认参数只能写在函数声明中{array=(int*)malloc(sizeof(int)*n);if(nullptr==array){perror("malloc申请空间失败");return;}capacity=n;top=0;}

注意成员函数名前要加类名::,告诉编译器这个函数属于哪个类域

2、类的访问限定符

  1. public(公有)
  2. private(私有)
  3. protected(私有 )

说明:

  • public修饰的成员,在类外面可以直接被访问
  • protect和private修饰的成员在类外不能直接被访问,protected和private是一样的
  • class定义成员没有被访问限定符修饰时默认为private,struct默认为public
  • 一般成员变量都会被限制为private/protected,需要给别人使用的成员函数会放为public

3、类域

类定义了一个新的作用域,类的所有成员都在类的作用域中,

#include<iostream>using namespace std;class Stack{public:// 成员函数voidInit(intn=4);private:// 成员变量int*array;size_tcapacity;size_ttop;};// 声明和定义分离,需要指定类域voidStack::Init(intn){array=(int*)malloc(sizeof(int)*n);if(nullptr==array){perror("malloc申请空间失败");return;}capacity=n;top=0;}intmain(){Stack st;st.Init();return0;}
  • 如果Init不指定类域Stack,那么编辑器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪里,就会报错。
  • 指定类域Stack,就会去对应的类域里面搜索,当类域里面也没有时,最后回到全局搜索,如果全局没有,编译就会报错

4、类的实例化

实例化概念用类类型在物理内存中创建对象的过程,称为类的实例化

类是对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员,只是声明,没有分配空间。类和对象的关系可以看成,使用建筑设计图造出房子,类是设计图不能存数据实例化出的对象才能分配物理内存存储数据

class Date{public:voidInit(intyear,intmonth,intday){_year=year;_month=month;_day=day;}voidPrint(){cout<<_year<<"-"<<_month<<"-"<<_day<<endl;}private:int_year;//声明 没有空间//只能由实例化的对象分配为例内存存储数据int_month;int_day;};intmain(){Date d1;Date d2;//实例化对象出d1和d2//Init函数指针是一样的 不需要独立空间d1.Init(2022,1,1);d2.Init(2022,1,2);d1.Print();d2.Print();return0;}

错误示范:

Date._year=2026;

相当于在图纸里住人

5、类的对象

类对象的大小

#include<iostream>using namespace std;// 计算⼀下A/B/C实例化的对象是多⼤?//类中既有成员变量,又有成员函数class A{public:voidPrint(){cout<<_ch<<endl;}private:char_ch;int_i;};//类中只有成员函数class B{public:voidPrint(){//...}};//类中什么也没有(空类)class C{};intmain(){A a;B b;C c;cout<<"A的大小"<<sizeof(a)<<endl;cout<<"B的大小"<<sizeof(b)<<endl;cout<<"C的大小"<<sizeof(c)<<endl;return0;}

运行结果:

A的大小8
B的大小1
C的大小1

类的大小,实际就是该类中“成员变量”之和,要遵守内存对齐规则,没有成员变量的类,编译器通常会给一个字节来占位标识对象的存在

拿Date类举例,Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year/_month/_day存储各自的数据,但是d1和d2的成员函数Init指针却是一样的,如果存储在对象中,每次实例化时成员函数就重复存储一次,浪费空间,所以成员函数要放在公共代码区

以建房子为例,类是图纸,实例化的对象是按照图纸造出来的房子,成员变量相当于房子中的房间,而成员函数相当于公共场所,不需要建在每一个房子里,建在公共场所,大家都可以使用

注意:sizeof(类)和sizeof(对象)计算出来的结果一样

内存对齐规则:

  • 第一个成员在与结构体偏移量为0的地址处。
  • 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  • 注意:对齐数 = 编译器默认的⼀个对齐数 与 该成员大小的较小值。
  • 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
  • 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
class A{public:voidPrint(){}private:char_ch;int_i;};

编译器一次读取4个字节,在内存没对齐的情况下,读取时非常麻烦,要把第一个读取的字节去掉,再把第二次读取的和第一个字节拼接,而在内存对齐的情况下,只需读取2次即可

6、this指针

//定义一个日期类class Date{public:voidInit(intyear,intmonth,intday){_year=year;this->_month=month;this->_day=day;}voidPrint(){cout<<_year<<"/"<<_month<<"/"<<_day<<endl;}private:// 这⾥只是声明,没有开空间int_year;int_month;int_day;};intmain(){// Date类实例化出对象d1和d2Date d1;Date d2;// d1.Init(&d1, 2024, 3, 31);d1.Init(2024,3,31);//d1初始化d1.Print();//打印d2.Init(2024,7,5);//d2初始化d2.Print();//打印return0;}

输出结果:

2024/3/31
2024/7/5

那当d1调用Init和Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?

这里C++给了一个隐含的this指针解决这里的问题,编译器编译后,类的成员函数会在形参的第一个位置增加一个当前类类型的指针,叫做this,函数题中的有关成员变量的操作都需要this指针访问

比如:Print函数

voidPrint(){cout<<_year<<"/"<<_month<<"/"<<_day<<endl;}

真实原型为:

voidPrint(Date*constthis){cout<<this->_year<<"/"<<this->_month<<"/"<<this->_day<<endl;}

d1和d2是同一个函数,调用时传递的参数不同,所以调用谁,打印的就是谁的结果。C++规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针。

注意:

  • this存储在栈中,this指针和普通的函数形参一样,在函数调用的时候入栈,存储在函数的调用栈帧里
  • 在成员函数中,不能修改this指针的指向
  • this指针是成员函数隐藏的指针形参,不需要写this

1.下面程序编译运行结果是()
A、编译报错 B、运行崩溃 C、正常运行

#include<iostream>using namespace std;class A{public:voidPrint(){cout<<"A::Print()"<<endl;}private:int_a;};intmain(){A*p=nullptr;p->Print();return0;}
  • 定义一个A类型的指针p并置为空,调用p的成员函数Print
  • 不发生解引用,Print函数的地址不在对象中,所以不会报错
  • 正确答案为C,正常运行,因为没有访问任何类中的其他成员,根本没有使用this指针

2.下面程序编译运行结果是()
A、编译报错 B、运行崩溃 C、正常运行

#include<iostream>using namespace std;class A{public:voidPrint(){cout<<"A::Print()"<<endl;cout<<_a<<endl;}private:int_a;};intmain(){A*p=nullptr;p->Print();return0;}
  • 定义一个A类型的指针p并置为空,调用p的成员函数Print
  • 在Print函数中使用了其他成员_a,相当于this→_a,this是空指针,所以这就成为了解引用空指针,运行崩溃

如果文章中有错误或不足,欢迎大家指正交流。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询