C++起始之路——类和对象(上)

目录

1.类的定义

2.实例化

3.this指针


1.类的定义

1.1类定义格式

●class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或成员函数

●为了区分成员变量,一般习惯上成员变量会加一个特殊标识,如成员变量前或后加_ 或m(menber)开头,注意C++中这个并不是强制的,只是一些惯例

●C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化时struct中可以定义函数(struct中成员默认全为public),一般情况下推荐用class定义类

●定义在类中的成员函数默认为inline

class Stack{

public:

void Init(int n=4){

array=(int *)malloc(sizeof(int)*n);

if(nullptr==array){

perror("malloc fail");

return ;

}

capacity=n;

top=n;

}

private:

//成员变量

int *array;

int capacity;

int top;

};//分号不能少

class Date{

public:

void Init(int year,int month,int day){

_year=year;

_month=month;

_day=day;

}

private:

//为了区分成员变量,习惯加上特殊标识

int _year;

int _month;

int _day;

};

1.2访问限定符

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

●public修饰的成员在类外可以直接被访问;peotected和private修饰的成员在类外不能直接被访问

●访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,若后面没有访问限定符,作用域就到},代表类结束

●class定义成员没有被访问限定符修饰时默认为private,struct默认为public

●一般成员变量都会被限制为private/protected,需要给别人使用的成员函数会放为public

1.3类域

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

●类域影响的是编译的查找规则,如下程序中Init如果不指定类域Stack,那么编译器就把Init当成全局函数,编译时就找不到array等成员的声明/定义在哪里,就会报错。指定类域Stack,就知道Init是成员函数,当前局部作用域中找不到array等成员时,就会到类域中去找,若还找不到就会到全局域中查找

class Stack{

public:

//成员函数

void Init(int n=4);

private:

//成员变量

int *array;

size_t capacity;

size_t top;

};

//定义和声明分离,需要指定类域

void Stack::Init(int n){//缺省函数定义时不需要在指定变量的值

array=(int *)malloc(sizeof(int)*n);

if(nullptr==array){

perror("malloc fail");

return ;

}

capacity=n;

top=0;

}

2.实例化

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,d2;

};

2.2对象大小

类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量 。函数被编译后是一段指令,对象中每有办法存储,这些指令存储在一个单独的区域(代码段),若对象在中非要存储,只能是成员函数的指针。那对象中是否有存储指针的必要呢?Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year/_month/_day存储各自的数据,但是两个对象的成员函数Init/Print指针却是一样的,若实例化100个对象,那成员函数指针就重复存储100次,太浪费。函数指针不需要存储,函数指针是一个地址,调用函数被编译成汇编指令[call地址],其实编译器在编译链接时,就要找到函数的地址,不是在运行时找,只有动态多态是在运行时找,需要存储函数地址

内存对齐规则

●第一个成员在与结构体偏移量为0的地址处

●其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处

●注意:对齐数=编译器默认的一个对齐数与该成员大小的较小值

●VS中默认对齐数为8

●结构体中大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍

●若嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整数大小就是所有最大对齐(含嵌套结构体的对齐数)的整数倍

class A{

public:

void Print(){

cout<<_c;

}

private:

char _c;

int _n;

};

class B{

public:

void Print(){

cout<<_c;

}

}

class C{};

int main(){

A a;

B b;

C c;

cout<<sizeof(a)<<endl;

cout<<sizeof(b)<<endl;

cout<<sizeof(c)<<endl;

}

上述程序输出 8 1 1,为什么没有成员变量还要给1个字节呢?因为如果一个字节都不给,怎么表示对象存在过呢!所以这里给1字节,只是为了占位标识对象存在

3.this指针

●Date类中有Init与Print两个成员函数,函数体中没有关于不同对象的区分,当d1调用Init和Print函数时,该函数是如何知道访问的是d1对象还是d2对象?在C++中有一个隐含的this指针解决该问题

编译器编译后,类的成员函数默认都会在形参第一个位置,增加一个当前类类型的指针,叫做this指针。如:Date类的Init的真实原型为,void Init(Date *const this,int year,int month,int day)

●类成员函数中访问成员变量,本质都是通过this指针访问的,如:Init函数中给_year赋值,this->_year=year;

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

class Date{

public:

//void Init(Date* const this,int year,int month,int day)

void Init(int year,int month,int day){

_year=year;

this->_month=month;

this->_day=day;

}

void Print(){

cout<<_year<<'/'<<_month<<'/'<<_day<<endl;

}

private:

int _year;

int _month;

int _day;

};

int main(){

Date d1;

//d1.Init(&d1,2025,12,6);

d1.Init(2025,12,6);

d1.Print();

}

如下程序运行结果是(C)

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

class A{

public:

void Print(){

cout<<"Print()"<<endl;

}

private:

int _a;

};

int main(){

A* p=nullptr;//p虽然为空指针,但并没有解引用

p->Print();

return 0;

}

如下程序运行结果是(B)

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

class B{

public:

void Print(){

cout<<"Print()"<<endl;

//cout<<this->_b<<endl;,试图解引用

cout<<_b<<endl;

}

private:

int _b;

};

int main(){

B* p=nullptr;//p虽然为空指针,但并没有解引用

p->Print();

return 0;

}

注:this指针存在内存的栈区域(或寄存器)里。因为它作为调用成员参数的隐含参数传递

相关推荐
老朱佩琪!2 小时前
在Unity中实现状态机设计模式
开发语言·unity·设计模式
FuckPatience2 小时前
C# BinarySearch 的返回值
开发语言·数据结构·c#
尼古拉斯·纯情暖男·天真·阿玮2 小时前
[JavaEE初阶] 进程和线程的区别和联系
java·开发语言
草莓熊Lotso2 小时前
《算法闯关指南:动态规划算法--斐波拉契数列模型》--04.解码方法
c++·人工智能·算法·动态规划
沐知全栈开发2 小时前
TypeScript Array(数组)
开发语言
1nv1s1ble2 小时前
[c++] cpp快速添加sqlite_orm
c++·sqlite
陶陶name2 小时前
Metal Compute Pipeline:Metal-C++ 环境配置与简单算子实现
开发语言·c++
认真敲代码的小火龙2 小时前
【JAVA项目】基于JAVA的宿舍管理系统
java·开发语言·课程设计
无限进步_2 小时前
寻找数组中缺失数字:多种算法详解与比较
c语言·开发语言·数据结构·算法·排序算法·visual studio