C++语言梗概

用久了其它语言会对c++特有的语法感到生疏,在这里记录一下

引用

引用可以看做常量型指针的语法糖。常用在函数参数的传递中,能提供更简洁的代码。 引用有以下特点:

  • 创建时候必须初始化
  • 只能指向一个对象,不能更改(常良新指针)
  • 不可能为NULL
void f(int* x){
    (*x)++;
}
void g(int& x){
    x++;
}

int main(){
    int a=0;
    f(&a);  //不够简介,但是能显示看出是传递了a的地址,会影响a的值。
    g(a);   //简洁,但是隐式传递了a的地址,只看函数调用,看不出会影响a的值。
}

常量引用,指针引用

不是了解,放在这里至少知道有这样的语法。

const int& q = 12; //常量引用,可以引用常量对象。
void increment(int*& i){
    i++;
}

纯虚函数

有纯虚函数的类,就是抽象类,编译器检测不让实例化。

class IBase{
    virtual void adust()=0;
}

枚举

//带范围的枚举,并制定枚举类型为`unsigned long`
enum class openunsigned long{
    input,
    output = 1844444444L
};
//全局枚举
enum {red,yellos }

运算符重载和自动类型转换

运算符重载

运算符重载可以定义成成员函数和全局函数。内置类型表达式的运算符是不能改的,用户自定义类型可以重载运算符。

运算符重载不能改变运算符的参数个数和优先级。运算符重载本质上也是函数调用,是一种语法糖。

全局操作符重载和友元

当左侧的运算符是别的对象的时候,可以重载全局运算符。并且在声明中标识friend,表示获得对类私有成员的访问权。<<左移操作符,经常被重载为表示输出的意思。

class A{
    int data[5];
    friend void g(A&,int);//全局函数
    friend void Y::f(x&);//成员函数
    friend class Z;
    friend ostream& operator<<(ostream& os,const A&a);//全局函数
}

ostream& operator<<(ostream& os,const A&a){
    for(int i=0;i<5;i++>){
        os<<5;
    }
    return os;
}

uint32 PackedHeader = 0;	
Reader << PackedHeader;

自动类型转换

表达式或函数调用在面对不合适的类型,编译器会尝试进行类型转换。有两种方式:指定类型的构造函数和重载的运算符。

class Pack{
    Handle(int*){}
}
void incomming(Pack p){}

int* Data = (int*)InData;
incomming(Data, Count)//自动先调用Pack(data)构造函数,再传参数。方式一

class newPack{
    int* data;
    operator Pack() const{
        return Pack(data)
        };
}
newPack pack
incomming(pack);//调用newPack的Pack()转换需要的类型。可以向内置类型转换。方式二

模板

模板分为类模板和函数模板。模板让我们不用手动复制代码,编译器帮我们为不同类型复制代码,并修改类名或函数名。模板有三种类型的参数:类型 ;编译时参数;其它模板。并且都可以指定模板值。



template<class T=int,int size=100> class Array{
    T data[size]
    void push();
}
//typename和class在这里是等价的
template<typename T ,template<class> class seq =Array> class Container{
    //当模板参数是其它模板时,需要对它实例化
    Array<T> array;
    //这里typename是告诉编译器,InterType是类型T的嵌入类型。
    typename T::InterType type;
}
//如果在头文件外定义类成员函数,也需要加上模板声明
template<class T=int,int=100>
void Array:push(){

}

模板的实例化都是编译期间,不同的值实例化,会产生不同的类。例如Array<int,10>和Array<int,20>是不同的类。

函数模板

//操作符重载也是一个函数
template<typename TypeBuff>
static TRepDataBufferBase operator+(TRepDataBufferBase InBuffer, const TypeBuff& Cmd)
{
	return InBuffer + Cmd.Offset;
}

class A{
public:
    int Offset;
}

TRepDataBufferBase InBuffer;
A a;
InBuffer + a;//函数模板直接调用

函数模板的实例化可以不用显式指定类型,编译器根据传入的参数推导出类型。在多个重载的函数模板中,会选择特化程度最高的函数来进行特化。

模板特化

特化(specialization)模板,可以为模板提供代码来使其特化。显式特化可以特化所有或者部分的模板参数,但都应该实现之前定义的全部接口。。如下特化了Array模板在持有bool类型时,可以用位操作来优化性能。这提供了一种“重载”类模板的方式

//全特化
template<> class Array<bool,100>{
    BitSet bitset;
    //实现其它方法
}

//半特化
template<int size=100> class Array<bool,size>{
    BitSet bitset;
    //实现其它方法
}

其它

别名

  • typedef 原类型名 别名
  • 模板别名 using 别名 attr(可选) = 类型标识 ; template < 模板形参列表 > using 别名 attr(可选) = 类型标识 ;
//模板別名
template<ERepDataBufferType DataType> using TConstRepDataBuffer = UE4_RepLayout_Private::TRepDataBufferBase<DataType, const uint8>;
using ConstOrNotVoid = typename TCopyQualifiersFromTo<ConstOrNotType, void>::Type;

元编程

模板有十分多的新奇用法。模板的特化甚至能在编译时实现循环和if-else的判定。提供了编译时编程的可能性,减少运行时的消耗。

template<int n> struct Factorial{
    enum{val = Factorial<n-1>::val*n};
};
template<> struct Factorial<0>{
    enum{val = 1};
};
int main(){
    cout << Factoral<12>::val << enl; //会在编译期间算出常数479001600
}

可变参数

C++11的新特性–可变模版参数。并且用模板的方式递归调用展开参数包 。

template<typename... ParamTypes>
static void SendParams(FControlChannelOutBunch& Bunch, ParamTypes&... Params) {}

template<typename FirstParamType, typename... ParamTypes>
static void SendParams(FControlChannelOutBunch& Bunch, FirstParamType& FirstParam, ParamTypes&... Params)
{
    Bunch << FirstParam;
    SendParams(Bunch, Params...);
}

编译器解析模板会做很多推导,当有二义性的时候,就会编译报错。

Updated: