C++:类、对象、this指针、内联函数


现实世界中的实体可以抽象出类别的概念。对应于计算机世界就有一个(class)的概念,因为类是一个抽象的概念的对应体,所以计算机不给它分配内存,只有当类实例化为对象时,给对象分配内存
在这里插入图片描述
类是设计的产物,对象是对类的实例化,设计类型时并没有给其开辟空间,实例化为对象后开空间,我们操作的是对象,而不是类

C++类的设计

C++中,类是一种数据类型,客观事物是复杂的,要描述它必须从多方面进行,也就是用不同的数据类型来描述不同的方面。类把数据(事物的属性)和函数(事物的行为、操作)封装为一个整体,如商场中的商品可以这样描述:

  • 商品名称(用字符串描述),该商品数量(用整数描述), 该商品单价(用浮点数描述),该商品总价(用浮点数描述)。
    class CGoods
    {
    public:
    	char Name[21];
    	int Amount;
    	float Price;
    	float Total_value;
    };//最后的分号不可少
    
  • 关键字class是数据类型说明符,指出下面说明的是类。标识符CGoods是商品这个类的类型名。花括号中是构成类体的一系列成员,关键字public是一种访问限定符

访问限定符有三种:private,protected、public,如果在类起始点无访问说明符号,系统默认为private

设计一个类的一般格式为:

class 类名
{
private:
	成员表1;
public:
	成员表2;
protected:
	成员表3;
};

成员函数的定义

函数定义通常在类的说明之后进行,其格式如下:

返回值类型  类名::函数名(参数表)
{
	//函数体
}

:: 称为作用域解析运算符,它指出该函数是属于那一个类的成员函数

对象(instance)


对象是类的实例。声明一种数据类型只是告诉编译系统该数据类型的构造,并没有预定内存。设计出来的类只是一个样板,以此样板可以在内存中开辟出同样结构的实例——对象。

创建类的对象可以有两种方法:

  1. 直接定义类的实例对象,创建在栈区:CGoods Car; int I;
  2. 动态创建类的对象,创建在堆区:CGoods *p = new CGoods();

有两种方法存储对象

第一种方法是为每一个对象都分配全套的内存来存属性和方法,包括安放成员数据的数据区和安放成员函数的代码区
在这里插入图片描述

但是区别同一个类的各个不同对象的属性是由数据成员决定的,不同对象的数据成员的内容是不一样的;而行为(操作)是用函数来描述的,这些操作的代码对所有的对象都是一样的,因而为每个对象都存储一份函数代码会造成空间上的浪费

第二种方法仅为每个对象分配一个数据区,代码区(放成员函数的区域)为各对象所共用
在这里插入图片描述
C++采用第二种方法存储对象。不同的对象有各自的属性,但是方法都是同一个方法,那么方法是怎么区别它操纵的到底是哪个对象的数据呢?这就是this指针的来源与用处所在

1.C++面向对象在内存中是如何分布的

  • 对于一个对象而言,成员变量和成员函数是分开存放的
    • 成员函数位于代码段,所有的类对象共有
    • 成员变量为每一个对象独有,位于内存中
  • 类对象在内存中的分布和struct完全相同
  • 对于继承,子类的对象是在父类的对象的基础上,加上子类自己的成员

2.设计类型时,这个类型存放在哪个位置

  • 类型定义(没有成员函数),编译的时候,放在编译器的符号表里,如果这个类型既没有静态成员变量,也没有定义变量常量(包括引用),也没有被函数使用(不是函数参数表中某个参数类型的一部分),没有用来定义其他类型的成员变量(常量)且不是一个嵌套类的成员,那么编译后的代码中,一般不会包含这个类型的信息.
    也就是说符号表这部分,可能会被丢弃
    结构定义也是一种类型定义. 类型定义本身.相当于声明

this指针


C++编译器对C++编译的时候,有三个步骤:

  1. 识别类里面的属性成员
  2. 识别方法,只识别方法的声明
  3. 改写类的方法(函数)成员

改写的具体有如下
对于如下方法:

void CGoods::RegisterGoods(char name[], int amount, float price)
{
	strcpy(Name, name);
	Amount = amount;
	Price = price;
}

编译器会改写为:

void CGoods::RegisterGoods(CGoods *const this, char name[],int amount,float price)
{
	strcpy(this->Name, name);
	this->Amount = amount;
	this->Price = price;
}

在类里面识别到的属性和类成员函数属性同名时,就会加上this指针,这是编译器编译时自动加的,我们也可以手动加,编译时系统就不给你加了

在主函数中调用类的成员函数时:

int main()
{
	CGoods x;
	CGoods y;
	x.RegisterGoods("C++",16,94);
	y.RegisterGoods("Java",10,88);
}

调用的那两句就等价于

RegisterGoods(&x,"C++",16,94);
RegisterGoods(&y,"Java",10,88);

这样,通过this指针就解决了上述的问题:“ 不同的对象有各自的属性,但是方法都是同一个方法,那么方法是怎么区别它操纵的到底是哪个对象的数据呢 ”

下面给出this指针的基本定义

this指针是类的一个自动生成、自动隐藏的私有成员,它存在于类的非静态成员函数中,指向被调用函数所在的对象。全局仅有一个this指针,当一个对象被创建时,this指针就存放指向对象数据的首地址。

每一个对象都隐式包含一个指针,指向对象自身。当对象调用成员函数时,会默认将对象自身传递给该函数,在函数体内不直接使用该对象名,而是使用this指针,即this指针指向该对象自身,即指向调用者

什么时候需要明确的写出this指针

防止命名冲突时可以显示地指出this指针,还有想将对象本身的指针或者引用给别的函数时就需要明确的写出this指针

内联函数


直接在类内定义的函数或用inline关键字修饰的定义在类外的函数称为内联函数,内联函数在调用时直接拷贝一份过去,提高了效率,以空间换时间

如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方,因而对内联函数进行任何修改,都需要重新编译函数的所有客户端

inline关键字只是给编译器内联的建议,至于会不会真的内联,编译器会根据函数具体规模权衡时间空间效率之后选择是否内联

定义为内联的意义

当函数体积小、常被调用、所从事的计算并不复杂时可以被定义为内联函数

C++处理内联函数方法是编译时将函数调用替换成内联函数的代码,程序执行时没有发生实际的函数调用,这样相当于用空间去换时间,提高了性能,因此一般将函数体较短,需频繁调用的函数定义成内联函数,这样代码空间不会膨胀的很厉害,同时程序的执行效率也会提高

内联函数与宏方式定义的有什么区别

1)宏是预编译阶段处理的,纯粹是字符串替换,没有任何的类型检查等,十分的不安全;而inline内联函数的处理是发生在编译阶段的,有完整的语句类型检查,比宏更安全

2)宏是无法调试的,inline函数在debug版本下和普通函数一样,出了问题很方便进行断点调试,定位问题

3)大量的宏很不方便去阅读源码,inline函数和普通函数一样,结构模块化清晰,方便阅读代码

4)用宏来代替函数定义,替换后还是一个正常的函数调用,有函数调用开销(栈帧开辟和回退);而用inline内联函数是在编译时期,在函数调用点,把函数的代码直接展开,省去了函数调用的开销,代码运行效率高

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值