this 指针,inline 内联函数,volatile关键字,assert宏,sizeof,#pragma pack(n)


本文是对该 博客的学习笔记,鸣谢博主

总结:

  • this指针是类对象成员函数在被调用的时候,隐式地传递给成员函数的,用于在成员函数体中给类对象的成员变量。
  • inline是建议编译器做内联,注意是建议,用于提高程序运行效率
  • volatile修饰的变量不会被编译器优化,volatile和const一样是类型修饰符
  • sizeof对数组,得到整个数组所占空间大小; sizeof对指针,得到指针本身所占空间大小

this指针

this指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向调用该成员函数的那个对象。
当对一个对象调用成员函数时,编译程序先将对象的地址赋值给this指针,然后调用成员函数,每次成员函数存取数据成员时,都隐式使用this指针。
当一个成员函数被调用时,自动向它传递一个隐含的参数,该参数是一个指向这个成员函数所在的对象的指针。

this指针被隐含地声明为:ClassName *const this,这意味着不能给this指针赋值;在ClassName类的const成员函数中,this指针的类型为:const ClassName *const,这说明this指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作)

this并不是一个常规变量,而是个右值,所以不能取得this的地址(不能&this)

在以下场景中,经常需要显式引用this指针:
为实现对象的链式引用;
为避免对同一对象进行赋值操作;
在实现一些数据结构时,如list

inline内联函数

特征
相当于把内联函数里面的内容写在调用内联函数处
相当于不用执行进入函数的步骤,直接执行函数体
相当于宏,却比宏多了类型检查,真正具有函数特性
编译器一般不内联包含循环、递归、switch等复杂操作的内联函数
在类声明中定义的函数,除了虚函数的其他函数都会自动隐式地当成内联函数
使用

//声明1(加inline,建议使用)
inline int functionName(int first, int second, ...);

//声明2(不加inline)
int functionName(int first, int second, ...);

//定义
inline int functionName(int first, int second, ...){/****/}

//类定义,隐式内联
class A{
	int doA(){return 0;}//隐式内联
}

//类外定义,需要显式内联
class A{
	int doA();
}
inline int A::doA(){return 0;}//需要显式内联

编译器对inline函数的处理步骤
将inline函数体复制到inline函数调用点处
为所用inline函数中的局部变量分配内存空间
将inline函数的输入参数和返回值映射到调用方法的局部变量空间中
如果inline函数有多个返回点,将其转变为inline函数代码块末尾的分支(使用GOTO)

优缺点
优点
内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收、结果返回等,从而提高程序运行速度。
内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会
在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能
内联函数在运行时可调试,而宏定义不可以
缺点
代码膨胀。内联是以代码膨胀(复制)为代价,消除函数调用带来的开销。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收货会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
inline函数无法随着函数库升级而升级。inline函数的改变需要重新编译,不像non-inline可以直接链接。
是否内联,程序员不可控。内联函数只是对编译器的建议,是否对函数内联,决定权在编译器

虚函数(virtual)可以是内联函数吗?
虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联
内联是在编译时建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联
inline virtual唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如:Base::who()),这只有在编译器具有实际对象而不是队形额指针或引用时才会发生

虚函数的内联使用

#include<iostream>
using namespace std;

class Base{
public:
	inline virtual void who(){
		cout<<"I am Base\n";
	}
	virtual ~Base(){}
};

clase Derived : public Base{
public:
	inline void who(){//不写inline时隐式内联
		cout<<"I am Derived\n";
	}
};

int main(){
	//此处的虚函数who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,
	//但最终是否内联取决于编译器
	Base b;
	b.who();
	
	//此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联
	Base *ptr = new Derived();
	ptr->who();
	
	//因为Base有虚析构函数(virtual ~Base(){}),
	//所以delete时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄露
	delete ptr;
	ptr = nullptr;
	
	system("pause");
	return 0;
}

volatile关键字

volatile int i = 10;

volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其他线程等)更改。所以使用volatile告诉编译器不应对这样的对象进行优化。

volatile关键字声明的变量,每次访问时都必须从内存中取出值(没有被volatile修饰的变量,可能由于编译器的优化,从CPU寄存器中取值)
const可以是volatile(如只读的状态寄存器)
指针可以是volatile

assert()

Assert是一个预处理宏,作用于一条表示条件的表达式。当未定义预处理变量NDEBUG时,assert对条件表达式求值,如果条件为假,输出一条错误信息并终止当前程序的执行
预处理宏,类似于内联函数的一种预处理功能,除了assert之外,现代C++程序很少再使用预处理宏了。

断言,是宏,而非函数。assert宏的原型定义在<assert.h>(C)<cassert>(C++)中,其作用是如果它的条件返回错误,则终止程序执行。可以通过定义NDEBUG来关闭assert,但是需要在源代码的开头,include<assert.h>之前

assert()使用

#define NDEBUG		//加上这行,则assert不可用
#include<assert.h>

assert(p!=NULL);	//assert不可用

sizeof()

sizeof对数组,得到整个数组所占空间大小
sizeof对指针,得到指针本身所占空间大小

#pragma pack(n)

设定结构体、联合以及类成员变量以n字节方式对其
使用

#progma pack(push) 	//保存对齐状态
#progma pack(4)		//设定为4字节对齐

struct test{
	char m1;
	double m4;
	int m3;
};

#pragma pack(pop)	//恢复对齐状态
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值