在大网站做网页广告需要多少钱,wordpress如何返回之前更新的版本,赤水网站建设,网站维护作用注意#xff1a; 本内容为摘抄网上的学习资料#xff0c;作为个人笔记使用#xff0c;如有侵权, 立刻删除。
C语言特性
1.关键字
#xff08;1#xff09;static
static全局变量和普通全局变量 面试高频指数#xff1a;★★★☆☆
相同点#xff1a;
存储方式 本内容为摘抄网上的学习资料作为个人笔记使用如有侵权, 立刻删除。
C语言特性
1.关键字
1static
static全局变量和普通全局变量 面试高频指数★★★☆☆
相同点
存储方式普通全局变量和 static 全局变量都是静态存储方式编译时分配内存。
不同点
作用域普通全局变量的作用域是整个源程序当一个源程序由多个源文件组成时普通全局变量在各个源文件中都是有效的静态全局变量则限制了其作用域即只在定义该变量的源文件内有效在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域限于一个源文件编译模块内只能为该源文件内的函数公用因此可以避免在其他源文件中引起错误。
例如在a.c中定义了static int a10; 那么在b.c中用extern int a是拿不到a的值得a的作用域只在a.c中。 初始化 static修饰普通变量修改变量的存储区域和生命周期使变量存储在静态区在 main 函数运行前就分配了空间如果有初始值就用初始值初始化它如果没有初始值系统用默认值比如整型的0初始化它。用static声明局部变量-------局部变量指在代码块 {} 内部定义的变量只在代码块内部有效作用域用static声明局部变量时则改变变量的存储方式生命期使变量成为静态的局部变量即编译时就为变量分配内存直到程序退出才释放存储单元。这样使得该局部变量有记忆功能可以记忆上次的数据不过由于仍是局部变量因而只能在代码块内部使用作用域不变。修饰成员变量在类中静态成员可以实现多个对象之间的数据共享并且使用静态数据成员还不会破坏隐藏的原则即保证了安全性。因此静态成员是类的所有对象中共享的成员而不是某个对象的成员。对多个对象来说静态数据成员只存储一处供所有对象共用 修饰普通函数表明函数的作用范围仅在定义该函数的文件内才能使用。在多人开发项目时为了防止与他人命名空间里的函数重名可以将函数定位为 static。 如果想要其他文件可以引用本地函数则要在函数定义时使用关键字extern表示该函数是外部函数可供其他文件调用。另外在要引用别的文件中定义的外部函数的文件中使用extern声明要用的外部函数即可。 修饰成员函数 静态成员函数和静态数据成员一样它们都属于类的静态成员它们都不是对象成员。因此对静态成员的引用不需要用对象名。 在静态成员函数的实现中不能直接引用类中说明的非静态成员可以引用类中说明的静态成员这点非常重要。 如果静态成员函数中要引用非静态成员时可通过对象来引用。从中可看出调用静态成员函数使用如下格式类名::静态成员函数名(参数表);
返回函数中静态变量的地址会发生什么
面试高频指数★★☆☆☆
#include iostream
using namespace std;int * fun(int tmp){static int var 10;var * tmp;return var;
}int main() {cout *fun(5) endl;return 0;
}/*
运行结果
50
*/说明上述代码中在函数 fun 中定义了静态局部变量 var使得离开该函数的作用域后该变量不会销毁返回到主函数中该变量依然存在从而使程序得到正确的运行结果。但是该静态局部变量直到程序运行结束后才销毁浪费内存空间。
静态变量什么时候初始化 初始化只有一次但是可以多次赋值在主程序之前编译器已经为其分配好了内存。 静态局部变量和全局变量一样数据都存放在全局区域所以在主程序之前编译器已经为其分配好了内存但在C和C中静态局部变量的初始化节点又有点不太一样。在C中初始化发生在代码执行之前编译阶段分配好内存之后就会进行初始化所以我们看到在C语言中无法使用变量对静态局部变量进行初始化在程序运行结束变量所处的全局内存会被全部回收。 而在C中初始化时在执行相关代码时才会进行初始化主要是由于C引入对象后要进行初始化必须执行相应构造函数和析构函数在构造函数或析构函数中经常会需要进行某些程序中需要进行的特定操作并非简单地分配内存。所以C标准定为全局或静态对象是有首次用到时才会进行构造并通过atexit()来管理。在程序结束按照构造顺序反方向进行逐个析构。所以在C中是可以使用变量对静态局部变量进行初始化的。
static 静态成员变量
面试高频指数★★★★★
静态成员变量是在类内进行声明在类外进行定义和初始化在类外进行定义和初始化的时候不要出现 static 关键字和private、public、protected 访问规则。静态成员变量相当于类域中的全局变量被类的所有对象所共享包括派生类的对象。 疑问派生类是什么静态成员变量可以作为成员函数的参数而普通成员变量不可以。
#include iostream
using namespace std;class A
{
public:static int s_var;int var;void fun1(int i s_var); // 正确静态成员变量可以作为成员函数的参数void fun2(int i var); // error: invalid use of non-static data member A::var
};
int main()
{return 0;
}静态数据成员的类型可以是所属类的类型而普通数据成员的类型只能是该类类型的指针或引用。
疑问指针 引用 分别是什么意思
#include iostream
using namespace std;class A
{
public:static A s_var; // 正确静态数据成员A var; // error: field var has incomplete type AA *p; // 正确指针A var1; // 正确引用
};int main()
{return 0;
}static 静态成员函数 静态成员函数不能调用非静态成员变量或者非静态成员函数因为静态成员函数没有 this 指针必须通过类名才能访问。静态成员函数做为类作用域的全局函数。 疑问this指针是什么怎么用 静态成员函数不能声明成虚函数virtual、const 函数和 volatile 函数。 疑问虚函数、const 函数、volatile 函数 是什么
2) volatile
volatile 的作用是否具有原子性对编译器有什么影响
面试高频指数★★☆☆☆ volatile 关键字是一种类型修饰符用它声明的类型变量表示可以被某些编译器未知的因素操作系统、硬件、其它线程等更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化(意思就是告诉编译器不要忽略这样的对象)。 volatile 关键字声明的变量每次访问时都必须从内存中取出值没有被 volatile 修饰的变量可能由于编译器的优化从 CPU 寄存器中取值保证对特殊地址的稳定访问 const 可以是 volatile 如只读的状态寄存器 指针可以是 volatile volatile不具有原子性。 注意 可以把一个非volatile int赋给volatile int但是不能把非volatile对象赋给一个volatile对象。除了基本类型外对用户定义类型也可以用volatile类型进行修饰。C中一个有volatile标识符的类只能访问它接口的子集一个由类的实现者控制的子集。用户只能用const_cast来获得对类型接口的完全访问。此外volatile向const一样会从类传递到它的成员。
什么情况下一定要用 volatile 能否和 const 一起使用
面试高频指数★★☆☆☆
使用 volatile 关键字的场景 当多个线程都会用到某一变量并且该变量的值有可能发生改变时需要用 volatile 关键字对该变量进行修饰 中断服务程序中访问的变量或并行设备的硬件寄存器的变量最好用 volatile 关键字修饰。
volatile 关键字和 const 关键字可以同时使用某种类型可以既是 volatile 又是 const 同时具有二者的属性。
关键字volatile有什么含意?
答案一个定义为volatile的变量是说这变量可能会被意想不到地改变这样编译器就不会去假设这个变量的值了。精确地说就是优化器在用到这个变量时必须每次都小心地重新读取这个变量的值而不是使用保存在寄存器里的备份。
volatile关键字
volatile int i 10; volatile 关键字是一种类型修饰符用它声明的类型变量表示可以被某些编译器未知的因素操作系统、硬件、其它线程等更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。volatile 关键字声明的变量每次访问时都必须从内存中取出值没有被 volatile 修饰的变量可能由于编译器的优化从 CPU 寄存器中取值const 可以是 volatile 如只读的状态寄存器指针可以是 volatile
const, volatile区别
1const含义是“请做为常量使用”而并非“放心吧那肯定是个常量”是不可修改的只读变量。 volatile的含义是“请不要做自以为是的优化这个值可能变掉的”而并非“你可以修改这个值”。
2const只在编译期有用在运行期无用
const在编译期保证在C的“源代码”里面没有对其修饰的变量进行修改的地方如有则报错编译不通过而运行期该变量的值是否被改变则不受const的限制。
volatile在编译期和运行期都有用
在编译期告诉编译器请不要做自以为是的优化这个变量的值可能会变掉
在运行期每次用到该变量的值都直接从内存中取该变量的值。
3const, volatile同时修饰一个变量
合法性
“volatile”的含义并非是“non-const”volatile 和 const 不构成反义词所以可以放一起修饰一个变量。
同时修饰一个变量的含义表示一个变量在程序编译期不能被修改且不能被优化在程序运行期变量值可修改但每次用到该变量的值都要从内存中读取以防止意外错误。
3extern
请你来说一说extern“C”
参考回答:
被 extern 限定的函数或变量是 extern 类型的被 extern C 修饰的变量和函数是按照 C 语言方式编译和链接的 extern C 的作用是让 C 编译器将 extern C 声明的代码当作 C 语言代码处理可以避免 C 因符号修饰导致代码不能和C语言库中的符号进行链接的问题。
extern “C” 使用
#ifdef __cplusplus
extern C {
#endifvoid *memset(void *, int, size_t);#ifdef __cplusplus
}
#endifextern C为什么需要
原因c和c 对同一个函数经过编译后生成的函数名是不同的由于C 支持函数重载因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中而不仅仅是函数名而C语言并不支持函数重载因此编译C语言代码的函数时不会带上函数的参数类型一般只包括函数名。如果在c 中调用一个使用c语言编写的模块中的某个函数那么c 是根据c 的名称修饰方式来查找并链接这个函数那么就会发生链接错误。
extern 关键字作用
基本解释extern可以置于变量或者函数前以标示变量或者函数的定义在别的文件中提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。
也就是说extern有两个作用。第一当它与C一起连用时如: extern “C” void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C的。
第二当extern不与C在一起修饰变量或函数时如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字其声明的函数和变量可以在本模块或其他模块中使用记住**它是一个声明不是定义!**也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时它只要包含A模块的头文件即可,在编译阶段模块B虽然找不到该函数或变量但它不会报错它会在连接时从模块A生成的目标代码中找到此函数。
4const
面试高频指数★★★☆☆
作用
const 修饰成员变量定义成 const 常量相较于宏常量可进行类型检查节省内存空间提高了效率。const 修饰函数参数使得传递过来的函数参数的值不能改变。const 修饰成员函数使得成员函数不能修改任何类型的成员变量mutable 修饰的变量除外也不能调用非 const 成员函数因为非 const 成员函数可能会修改成员变量。
在类中的用法
const 成员变量 const 成员变量只能在类内声明、定义在构造函数初始化列表中初始化。 const 成员变量只在某个对象的生存周期内是常量对于整个类而言却是可变的因为类可以创建多个对象不同类的 const 成员变量的值是不同的。因此不能在类的声明中初始化 const 成员变量类的对象还没有创建编译器不知道他的值。
const 成员函数
不能修改成员变量的值除非有 mutable 修饰只能访问成员变量。 不能调用非常量成员函数以防修改成员变量的值。 疑问下面的代码 A(int tmp) : var(tmp) {}没看懂
#include iostream
using namespace std;class A
{
public:int var;A(int tmp) : var(tmp) {}void c_fun(int tmp) const // const 成员函数{var tmp; // error: assignment of member A::var in read-only object. 在 const 成员函数中不能修改任何类成员变量。 fun(tmp); // error: passing const A as this argument discards qualifiers. const 成员函数不能调用非 const 成员函数因为非 const 成员函数可能会修改成员变量。}void fun(int tmp){var tmp;}
};int main()
{return 0;
}请你来说一下C里是怎么定义常量的常量存放在内存的哪个位置
常量在C里的定义就是一个top-level const加上对象类型常量定义必须初始化。
对于局部对象常量存放在栈区对于全局对象常量存放在全局/静态存储区。对于字面值常量常量存放在常量存储区。 疑问字面值常量是什么
const作用 “只读”
const修饰变量以下两种定义形式在本质上是一样的。它的含义是const修饰的类型为TYPE的变量value是不可变的。
TYPE const ValueName value;const TYPE ValueName value;修饰指针
指向常量的指针pointer to const自身是常量的指针常量指针const pointer
修饰引用
指向常量的引用reference to const如果用于形参类型即避免了拷贝又避免了函数对值的修改没有 const reference因为引用只是对象的别名引用不是对象不能用 const 修饰
修饰成员函数说明该成员函数内不能修改成员变量。const修饰的成员函数表明函数调用不会对对象做出任何更改事实上如果确认不会对对象做更改就应该为函数加上const限定这样无论const对象还是普通对象都可以调用该函数。
const 使用 为了方便记忆可以想成被 const 修饰在 const 后面的值不可改变如下文使用例子中的 p2、p3 实际上const和*的优先级相同且是从右相左读的即“右左法则” 疑问下面的 ** 已经让我懵逼
比如int*p;//读作p为指针指向int所以p为指向int的指针int*const p;//读作p为常量是指针指向int所以p为指向int的常量指针 p不可修改int const *p;//p为指针指向常量为int所以p为指向int常量的指针 *p不可修改int ** const p; //p为常量指向指针指针指向int所以p为指向int型指针的常量指针p不可修改const int **p;//指向常量指针的指针int const**p; //p为指针指向指针指针指向常量int所以p为指针指向一个指向int常量的指针 **p为int不可修改int * const *p ; //p为指针指向常量该常量为指针指向int所以p为指针指向一个常量指针*p为指针不可修改int ** const *p; //p为指针指向常量常量为指向指针的指针p为指针指向常量型指针的指针*p为指向指针的指针不可修改int * const **p; //p为指针指向一个指针1指针1指向一个常量常量为指向int的指针即p为指针指向一个指向常量指针的指针 **p为指向一个int的指针不可修改 为什么 const A q a; // 指向常对象的引用
// 类
class A
{
private:const int a; // 常对象成员可以使用初始化列表或者类内初始化public:// 构造函数A() : a(0) { };A(int x) : a(x) { }; // 初始化列表// const可用于对重载函数的区分int getValue(); // 普通成员函数int getValue() const; // 常成员函数不得修改类中的任何数据成员的值
};void function()
{// 对象A b; // 普通对象可以调用全部成员函数const A a; // 常对象只能调用常成员函数const A *p a; // 指针变量指向常对象const A q a; // 指向常对象的引用 // 指针char greeting[] Hello; //字符串hello保存在栈区可以通过greeting去修改const char * arr 123; //字符串123保存在常量区const本来是修饰arr指向的值不能通过arr去修改但是字符串“123”在常量区本来就不能改变所以加不加const效果都一样char * brr 123; //字符串123保存在常量区这个arr指针指向的是同一个位置同样不能通过brr去修改123的值const char crr[] 123;//这里123本来是在栈上的但是编译器可能会做某些优化将其放到常量区
//确实经过简单测试const char crr[]123; crr[1]5;结果报错
//error: assignment of read-only location ‘crr[1]’char* p1 greeting; // 指针变量指向字符数组变量const char* p2 greeting; // 指针变量指向字符数组常量const 后面是 char说明不能通过p2修改greeting但是greeting在栈上可以通过其它方式修改比如下标和p1来修改char* const p3 greeting; // 自身是常量的指针指向字符数组变量const 后面是 p3说明 p3 指针自身不可改变即指针不能指向其它地址但可以修改其中的值//比如*(p21)c; 则报错assignment of read-only location ‘*(p2 1u)’const char* const p4 greeting; // 自身是常量的指针指向字符数组常量
}
函数中使用const
(1)const修饰函数参数
a.传递过来的参数在函数内不可以改变(无意义因为Var本身就是形参)void function(const int Var);b.参数指针所指内容为常量不可变void function(const char* Var);c.参数指针本身为常量不可变(也无意义因为char* Var也是形参)void function(char* const Var);d.参数为引用为了增加效率同时防止修改。修饰引用参数时void function(const Class Var); //引用参数在函数内不可以改变void function(const TYPE Var); //引用参数在函数内为常量不可变这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本, 然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性, 且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙.
(2)const 修饰函数返回值
const修饰函数返回值其实用的并不是很多它的含义和const修饰普通变量以及指针的含义基本相同。
const int fun1() //返回一个常数这个其实无意义因为参数返回本身就是赋值。const int * fun2() //返回一个指向常量的指针变量使用如下const int *pValue fun2(); //我们可以把fun2()看作成一个变量即指针内容不可变。int* const fun3() //返回一个指向变量的常指针int * const pValue fun2(); //我们可以把fun2()看作成一个变量即指针本身不可变。const与define
const
const 常量限定符声明的常量只读不允许修改在 C (不是 C) 中可以用 const 值声明数组长度对于全局 const 值C 中默认是内部链接(跟 static 一样只允许在本文件内可见)而不是 C 中的默认外部链接若想在其他文件中使用必须在其他文件中重新定义或将 const 值放在头文件中(默认是外部链接放在头文件中编译可能会出现错误默认是内部链接就不会出错)
define 编译阶段define 是在编译预处理阶段进行替换const 是在编译阶段确定其值。 安全性define 定义的宏常量没有数据类型只是进行简单的替换不会进行类型安全的检查const 定义的常量是有类型的是要进行判断的可以避免一些低级的错误。 内存占用define 定义的宏常量在程序中使用多少次就会进行多少次替换内存中有多个备份占用的是代码段的空间const 定义的常量占用静态存储区的空间程序运行过程中只有一份。 调试define 定义的宏常量不能调试因为在预编译阶段就已经进行替换了const 定义的常量可以进行调试。
C 中推荐使用 const 代替 #define 声明常量
const 能够明确指定常量类型const 能够用于更复杂的数据类型(如数组结构体和类)const 标识符遵循变量的作用域规则可以创建作用域为全局(仅在本文件中使用)、命名空间、函数或数据块的常量
5mutable
mutable的中文意思是“可变的易变的”跟constant既C中的const是反义词。在C中mutable也是为了突破const的限制而设置的。被mutable修饰的变量将永远处于可变的状态即使在一个const函数中。我们知道如果类的成员函数不会改变对象的状态那么这个成员函数一般会声明成const的。但是有些时候我们需要在const函数里面修改一些跟类状态无关的数据成员那么这个函数就应该被mutable来修饰并且放在函数后后面关键字位置。
6inline
面试高频指数★★★☆☆
作用
inline 是一个关键字可以用于定义内联函数。内联函数像普通函数一样被调用但是在调用时并不通过函数调用的机制而是直接在调用点处展开这样可以大大减少由函数调用带来的开销从而提高程序的运行效率。
特征
相当于把内联函数里面的内容写在调用内联函数处相当于不用执行进入函数的步骤直接执行函数体相当于宏却比宏多了类型检查真正具有函数特性宏是什么编译器一般不内联包含循环、递归、switch 等复杂操作的内联函数在类声明中定义的函数除了虚函数的其他函数都会自动隐式地当成内联函数。
使用方法
类内定义成员函数默认是内联函数 在类内定义成员函数可以不用在函数头部加 inline 关键字因为编译器会自动将类内定义的函数构造函数、析构函数、普通成员函数等声明为内联函数代码如下
#include iostream
using namespace std;class A{
public:int var;A(int tmp){ var tmp;} //这句不懂void fun(){ cout var endl;}
};int main()
{ return 0;
}类外定义成员函数若想定义为内联函数需用关键字声明 当在类内声明函数在类外定义函数时如果想将该函数定义为内联函数则可以在类内声明时不加 inline 关键字而在类外定义函数时加上 inline 关键字。
#include iostream
using namespace std;class A{
public:int var;A(int tmp){ var tmp;} void fun();
};inline void A::fun(){cout var endl;
}int main()
{ return 0;
}另外可以在声明函数和定义函数的同时加上 inline也可以只在函数声明时加 inline而定义函数时不加 inline。只要确保在调用该函数之前把 inline 的信息告知编译器即可。
inline 函数工作原理
面试高频指数★★☆☆☆ 内联函数不是在调用时发生控制转移关系而是在编译阶段将函数体嵌入到每一个调用该函数的语句块中编译器会将程序中出现内联函数的调用表达式用内联函数的函数体来替换。 普通函数是将程序执行转移到被调用函数所存放的内存地址当函数执行完后返回到执行此函数前的地方。转移操作需要保护现场被调函数执行完后再恢复现场该过程需要较大的资源开销。
编译器对 inline 函数的处理步骤
将 inline 函数体复制到 inline 函数调用点处为所用 inline 函数中的局部变量分配内存空间将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中如果 inline 函数有多个返回点将其转变为 inline 函数代码块末尾的分支使用 GOTO。
inline 优缺点
优点 inline定义的内联函数函数代码被放入符号表中在使用时进行替换内联函数同宏函数一样将在被调用处进行代码展开省去了参数压栈、栈帧开辟与回收结果返回等从而提高程序运行速度效率很高。 类的内联函数也是函数。编绎器在调用一个内联函数首先会检查参数问题保证调用正确像对待真正函数一样消除了隐患及局限性。内联函数相比宏函数来说在代码展开时会做安全检查或自动类型转换同普通函数而宏定义则不会。 inline可以作为类的成员函数。在类中声明同时定义的成员函数自动转化为内联函数因此内联函数可以访问类的成员变量宏定义则不能。 内联函数在运行时可调试而宏定义不可以。
缺点
代码膨胀。内联是以代码膨胀复制为代价消除函数调用带来的开销。如果执行函数体内代码的时间相比于函数调用的开销较大比如函数体内有循环那么效率的收获会很少。另一方面每一处内联函数的调用都要复制代码将使程序的总代码量增大消耗更多的内存空间。这种情况编译器可能会自动把它作为非内联函数处理。inline 函数无法随着函数库升级而升级。inline函数的改变需要重新编译不像 non-inline 可以直接链接。是否内联程序员不可控。内联函数只是对编译器的建议是否对函数内联决定权在于编译器。
内联函数一般可以 加快程序的执行速度 可能减小可执行文件的大小 可能增加可执行文件的大小 可能降低执行速度。
1和3很好理解在编译时期内联函数能将代码直接写入其被调用的地方这样就减少了入栈出栈的时间消耗但是如果调用内联函数的地方过多代码量也会随之增加增加了可执行文件的大小。
2为什么正确呢是因为如果调用普通函数的话编译器可能会产生更多的代码来实现压、出寄存器的代码对于简单的内联函数会这样。但如果优化器能顺序集成消除大量冗余代码的话对大函数也同样适用。
4呢如果可执行文件过大会频繁的出现内存的换入换出操作会使执行速度下降。
虚函数virtual可以是内联函数inline吗什么是虚函数
虚函数可以是内联函数内联是可以修饰虚函数的但是当虚函数表现多态性的时候不能内联。内联是在编译器建议编译器内联而虚函数的多态性在运行期编译器无法知道运行期调用哪个代码因此虚函数表现为多态性时运行期不可以内联。inline virtual 唯一可以内联的时候是编译器知道所调用的对象是哪个类如 Base::who()这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。
虚函数内联使用
#include iostream
using namespace std;
class Base
{
public:inline virtual void who(){cout I am Base\n;}virtual ~Base() {}
};
class 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;
} inline 与 typedef 与 define
typedef
类型重命名可以写在函数外部同样也可以函数内部它们的作用域不同可以提高代码的可读性typedef 可以分别为基本类型重命名、指针类型重命名、结构体类型重命名和函数指针类型重命名typedef 是关键字在编译时处理有类型检查功能用来给一个已经存在的类型一个别名但不能在一个函数定义里面使用 typedef 。
define
原理#define 作为预处理指令在编译预处理时进行替换操作不作正确性检查只有在编译已被展开的源程序时才会发现可能的错误并报错。#define 不仅可以为类型取别名还可以定义常量、变量、编译开关等。作用域#define 没有作用域的限制只要是之前预定义过的宏在以后的程序中都可以使用而 typedef 有自己的作用域。enum给int型常量起名字typedef给数据类型起名字宏定义也可以看做一种重命名
指针的操作typedef 和 #define 在处理指针时不完全一样
#include iostream
#define INTPTR1 int *
typedef int * INTPTR2;using namespace std;int main()
{INTPTR1 p1, p2; // p1: int *; p2: intINTPTR2 p3, p4; // p3: int *; p4: int *int var 1;const INTPTR1 p5 var; // 相当于 const int * p5; 常量指针即不可以通过 p5 去修改 p5 指向的内容但是 p5 可以指向其他内容。const INTPTR2 p6 var; // 相当于 int * const p6; 指针常量不可使 p6 再指向其他内容。return 0;
}C 中推荐使用 inline 代替 #define 声明函数
C 中使用 inline 定义内联函数(C 语言中一般是使用 #define 定义宏函数)宏函数是简单的文本替换不是真正的传参数如果不注意运算顺序很容易出错C 使用 inline 定义内联函数比定义宏函数可靠inline 定义的内联函数是真正的传递参数C 中 inline 可用于常规函数也可用于类方法宏函数的一个优点是无类型可用于任意类型运算都是有意义的在 C 中可创建内联函数模板实现这个功能