文章目录
内联函数和宏定义的区别
内联函数和普通函数相比可以加快程序运行的速度,因为不需要中断调用,在编译的时候,内联函数可以直接嵌入到目标代码中。
内联函数适用场景
-
使用宏定义的地方都可以使用
inline
函数。 -
作为类成员接口函数来读写类的私有成员或者保护成员,会提高效率。
内联函数一般只用于如下情况
- 一个函数不断被调用
- 函数只有简单的几行,且函数不包含
for,while,switch
等语句
为什么不能把所有的函数写成内联函数
内联函数以代码复杂为代价,它省去函数调用的开销来提高执行效率。所以一方面如果内联函数体内代码执行时间相比函数调用开销较大,则没有太大的意义;另一方面每一处内联函数的调用都要复制代码,消耗更多的内存空间。因此以下情况不宜使用内联函数。
- 函数体内的代码比较长,复制代码时将耗费更多代价
- 函数体内有循环,函数执行时间要比函数调用的开销大
主要区别
- 内联函数在编译时展开,宏在预编译时展开
- 内联函数直接嵌入到目标代码中,宏是简单的做文本替换
- 内联函数有类型检测,语法判断等功能,而宏没有
- 内联函数是函数,宏不是,内联函数具有重载等功能
- 宏定义时要注意书写(参数要括起来)否则容易出现歧义,内联函数不会产生歧义
- 内联函数代码被放到符号表中,使用时像宏一样展开,没有调用的开销,效率很高
- 在使用时,宏只做简单字符串的替换(编译前)。而内联函数可以进行参数类型检查(编译时),且具有返回值
- 内联函数可以作为某个类的成员函数,这样可以使用类的保护成员和私有成员。而当一个表达式涉及到类保护成员或私有成员时,宏就不能实现了。
构造函数,析构函数,虚函数可否声明为内联函数
首先,将这些函数声明为内联函数,在语法上没有错误。
构造函数和析构函数声明为内联函数是没有意义的
《effective c++》中阐述的是:将构造函数和析构函数声明为inline
是没有什么意义的,即编译器并不真正对声明为inline
的构造和析构函数进行内联操作,因为编译器会在构造和析构函数中添加额外的操作(申请/释放内存,构造/析构对象等),使得构造函数/析构函数并不像看上去的那么精简。其次,class
中的函数默认是Inline
的,编译器也只是有选择性的inline
,将构造函数和析构函数声明为内联函数是没有什么意义的。
将虚函数声明为inline,要分情况讨论
如果是用指向派生类的指针(多态性)调用声明为inline
的虚函数,不会内联展开,当用对象本身调用虚函数时,会内联展开,当然前提仍然是函数并不复杂的情况下。
如何用代码判断大小端存储
大端存储: 字数据的高字节存储在低地址中。
小端存储: 字数据的低字节存储在低地址中。
例如:32bit的数字0x12345678
小端模式中的存储方式为:
内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
---|---|---|---|---|
存放内容 | 0x78 | 0x56 | 0x34 | 0x12 |
大端模式中的存储方式为:
内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
---|---|---|---|---|
存放内容 | 0x12 | 0x34 | 0x56 | 0x78 |
判断方式
方式1:用union
联合体
#include <iostream>
using namespace std;
//union联合体的重叠式存储,联合体占用内存的空间为每个成员字节长度的最大值
union endian
{
int a;
char ch;
};
int main()
{
endian value;
value.a = 0x1234;
//a和ch公用4字节的内存空间
if(value.ch == 0x12)
cout << "big endian" << endl;
else if(value.ch == 0x34)
cout << "little endian" <<endl;
}
方式2:使用强制类型转换
#include <iostream>
using namespace std;
int main()
{
int a = 0x1234;
//由于int和char的长度不同,借助int型转换成char型, 只会留下低地址的部分
char c = (char)(a);
if(c == 0x12)
cout << "big endian" << endl;
else if(c == 0x34)
cout << "little endian" <<endl;
}
哪几种情况必须用到初始化成员列表?
- 初始化一个
const
成员 - 初始化一个
reference
成员 - 调用一个基类的构造函数,而该函数有一组参数
- 调用一个数据成员对象的构造函数,而该函数有一组参数。
变量声明和变量定义区别
- 声明仅仅是把变量声明的位置及类型提供给编译器,并不分配内存空间;定义要在定义的地方为其分配存储空间。
- 变量能且只能被定义一次,但是可以被多次声明。
extern int i; //声明了i
int j; //声明并定义了j
任何包含了显式初始化的声明即成为定义,如
extern double pi = 3.14; //定义
final 和override关键字
override
当在父类中使用了虚函数的时候,你可能需要在某个子类中对这个虚函数进行重写,以下方法都可以:
class A
{
virtual void foo();
}
class B : public A
{
void foo();
virtual void foo();
void foo() override;
}
如果不使用override
,当你手一抖,将foo()
写成了f00()
会怎么样呢?结果是编译器并不会报错,因为它并不知道你的目的是重写虚函数,而是把它当成了新的函数。如果这个虚函数很重要的话,那就会对整个程序不利。所以,override
的作用就出来了,它指定了子类的这个虚函数是重写父类的话,如果你名字不小心打错的话,编译器是不会通过的。
class A
{
virtual void foo();
}
class B : public A
{
virtual void f00(); //OK,这个函数是B新增的,不是继承的
virtual void fo0() override; //Error,加了override之后,这个函数一定是继承A的,
} //A找不到就报错
final
当不希望某个类被继承,或不希望某个虚函数被重写,可以在类名和虚函数后添加final
关键字,添加final
关键字后被继承或重写,编译器会报错。
例子如下:
class Base
{
virtual void foo();
};
class A : public Base
{
void foo() final; //foo被override并且是最后一个override,在其子类中不可以重写
};
class B final : A //指明B是不可以被继承的
{
void foo() override; //Error: 在A中已经被final了
};
class C : B //Error: B is final
{
};
在main执行之前和之后执行的代码可能是什么?
main函数执行之前
main函数执行之前,主要就是初始化系统相关资源:
- 设置栈指针
- 初始化静态
static
变量和global
全局变量,即.data
段的内容 - 将未初始化部分的全局变量赋初值:数值型
short,int,long
等为0,bool
为FALSE
,指针为NULL
等等,即.bss
段的内容 - 全局对象初始化,在
main
之前调用构造函数,这是可能会执行前的一些代码 - 将main函数的参数
argc,argv
等传递给main
函数,然后才真正运行main
函数
main函数执行之后
- 全局对象的析构函数会在
main
函数之后执行 - 可以用
atexit
注册一个函数,它会在main
之后执行
// C++ program to illusttrate
// atexit() function
#include <bits/stdc++.h>
using namespace std;
// Returns no value, and takes nothing as a parameter
void done()
{
cout << "Exiting Successfully"
<< "\n"; // Executed second
}
// Driver Code
int main()
{
int value;
value = atexit(done); //atexit返回零:表示注册成功
//atexit返回非零,表示注册失败
if (value != 0) {
cout << "atexit () function registration failed";
exit(1);
}
cout << " Registration successful"
<< "\n"; // Executed First
return 0;
}
/*
Registration successful
Exiting Successfully
*/