(1)C预处理,条件编译
(A)宏定义与宏替换
宏名一般大写,替换发生在编译之前,且是机械替换,不做语法检查,也不分配内存,不占用运行时间,只占用编译时间。
1、符号常量的宏定义和宏替换 :#define 标识符 字符串
#include<iostream> #define P 3+4 using namespace std; void main() { int a=2; cout<<a*P<<endl; //相当于a*3+4,而不是a*(3+4),机械替换 }
2、带有参数的宏定义和宏替换:#define 标识符(参数列表) 字符串
#include<iostream> #define FUN(a) a*a using namespace std; void main() { cout<<FUN(2+3)<<endl; //机械替换,2+3*2+3 }
(B)文件包含
#include的作用是把它后面所写的那个文件的内容,一字不改地包含到当前的文件中。
(C)条件编译
//格式 #if/ifdef/ifndef #elif #else #endif
一般用在头文件中,避免多重包含,在源程序中引入头文件,相当于把头文件的内容复制到源文件当中。
我们都知道,一个符号可以多次声明,但只能定义一次。那么头文件的引用就涉及到了这个问题,当一个函数fun()在头文件source中进行定义后,若类A中包含该文件,类B也包含该文件,而在源文件C中用到了A和B,#include"A" 和#include"B"时,source在C中包含了两次,会出现fun()的多次定义,导致错误。
#ifndef S #define S //类的定义 #endif
(2)_cpluscplus
_cpluscplus是c++中的定义,而c中没有该定义
int main() { #ifdef _cplusplus printf("This is c++ program"); #else printf("This is c program"); }
(3)NULL和nullptr
(A)
NULL是一个宏定义,在c和c++中的定义不同,c中NULL为(void*)0,而c++中NULL为整数0
//C语言中NULL定义 #define NULL (void*)0 //c语言中NULL为void类型的指针,但允许将NULL定义为0 //c++中NULL的定义 #ifndef NULL #ifdef _cpluscplus //用于判定是c++类型还是c类型 #define NULL 0 //c++中将NULL定义为整数0 #else #define NULL ((void*)0) //c语言中NULL为void类型的指针 #endif #endif
所以在C++中int *p=NULL; 实际表示将指针P的值赋为0,而c++中当一个指针的值为0时,认为指针为空指针。
(B)
nullptr是c++11中的关键字,表示空指针,是一个字面值常量,类型为std::nullptr_t,空指针常数可以转换为任意类型的指针类型。
在c++中(void *)不能转化为任意类型的指针,即 int *p=(void*)是错误的,但int *p=nullptr是正确的。
void fun(int i){cout<<"1";}; void fun(char *p){cout<<"2";}; int main() { fun(NULL); //输出1,c++中NULL为整数0 fun(nullptr);//输出2,nullptr为空指针常量,是指针类型 }
(4)C++静态成员
静态数据成员的类型可以是它所属类的类型,而非静态数据成员则受到限制,只能声明为它所属类的指针或引用。
class type { static type a; //ok,静态成员可以是不完全类型 type *b; //ok,指针成员可以是不完全类型 // type c; //error,数据成员必须是完全类型 public: //... };
静态成员可以作为默认实参,而非静态成员不能。
class student { static int a; public: void fun(int b=a); }
(5)C++引用
C++中规定,一旦定义引用就必须进行初始化,且不能在再被指定为其他变量的引用。
int a=3,b=1; int &ref=a; //ok,ref是a的引用 ref=b; //将b的值赋给a,而不是将ref绑定到变量b int &ref; ref=a; //error,必须在定义时初始化
引用和指针的区别:
(6)mutable和volatile关键字
(A)mutable
在C++中,mutable是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,甚至结构体变量或者类对象为const,其mutable成员也可以被修改。mutable在类中只能够修饰非静态数据成员。
#include <iostream> using namespace std; class test { mutable int a; int b; public: test(int _a,int _b) :a(_a),b(_b){}; void fun() const //fun是const 函数,不能修改类的对象的数据成员,但由于a被mutable修饰,可以修改,但不能修改b { a += b; } void print() { cout << a << "," << b << endl; } };
如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutable来修饰。
(B)volatile
volatile应该解释为“直接存取原始内存地址”比较合适,一般常用于多任务环境下各任务间共享的标志应该加volatile。
(7)final 和 override
(A)final
有时我们会定义这样一种类,我们不希望其他类继承它。C++ 11新标准提供了一种防止继承的方法,即在类名后跟一个关键字final。
class base final {/* */} //base不能作为基类 class Derived:base { /* */} //错误,base不能作为基类
此外,final还可以修饰类中的虚函数,表示类成员函数不可以在派生类中进行覆盖。
class base { virtual void fun() final {...}; } class derived:base { void fun(){...}; //error }
(B)override
对于基类中的虚函数,派生类可以选择覆盖或者不覆盖,对于选择覆盖的函数用override加以修饰,表示该函数覆盖了基类的虚函数。
class base { public: virtual void f1(); virtual void f2(); void f3(); } class derived:public base { public: void f1()override; //ok void f2(int) override; //error,基类中不存在f2(int)虚函数 void f3() override; //error,f3不是虚函数 }
注:final,override修饰的函数必须为虚函数。
(8)拷贝构造函数必须是一个引用
拷贝构造函数的参数必须是引用,参数传递的方式有两种,值传递和地址传递。
其中值传递即是拷贝原对象的一个副本作为实参,即参数传递的过程中也调用了拷贝构造函数,若拷贝构造函数的参数不是引用的话,会造成无穷递归的调用拷贝构造函数。
而引用是直接操作原对象,因此不会出现上述问题。
(9)模板的特化
模板分为类模板和函数模板,特化分为全特化和偏特化。
//类模板 template<typename T1, typename T2> class Test { public: Test(T1 i,T2 j):a(i),b(j){cout<<"模板类"<<endl;} private: T1 a; T2 b; }; template<> class Test<int , char> { public: Test(int i, char j):a(i),b(j){cout<<"全特化"<<endl;} private: int a; char b; }; template <typename T2> class Test<char, T2> { public: Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<<endl;} private: char a; T2 b; };
//函数模板 template<typename T1, typename T2> void fun(T1 a , T2 b) { cout<<"模板函数"<<endl; } //全特化 template<> void fun<int ,char >(int a, char b) { cout<<"全特化"<<endl; } //函数不存在偏特化:下面的代码是错误的 /* template<typename T2> void fun<char,T2>(char a, T2 b) { cout<<"偏特化"<<endl; } */
(10)const修饰符
(A)指向const常量的指针 和 const指针
//(1)指向const常量的指针:const int *p 或 int const *p //p是一个指针,指向const int类型的常量;指针指向的内容为常量,因此不能改变*p的值,但指针p可以改变 const int a=2; const int b=3; const int *p=&a; *p=4; //error,p指向常量a,不能修改 p=&b; //ok,p只要指向const int类型即可 //(2)const指针:int *const p //p是一个指针,指针p的值不能改变,但其指向的值可以改变 int a=2; int b=3; int *const p=&a; *p=4; //ok,p的内容可以改变 p=&b; //error,p是常指针,指针值不能修改 //(3)指向const常量的const指针:const int *const p 或 int const *const p //p是一个const指针,指针值和指向的对象的值都不允许修改 int a=2; int b=3; const int *const p=&a; *p=4; //error,p指向常量 p=&b; //error,p是常指针
(B)类的const成员
类的const成员包括const数据成员和const成员函数;
(1)const数据成员:和普通的const变量一样,定义时初始化,且不能修改;
(2)const成员函数: