本文章以<深入理解C++11>书为目录和例子,进行简要的测试如说明或者总结,后续再对特殊的,信息含量较大的特性进入深入研究。
#include <iostream>
#include <memory>
#include "tesh.h"
using namespace std;
//__func__宏,可以用在类中
struct TestStruct{
TestStruct() : name(__func__){}
const char* name;
};
//_Pragma操作符,用来替换#pragma预处理,这是个操作符,使用上更灵活。
//_Pragma("once")
//变长参数宏:先来看一个变长参数函数,原理是函数调用栈的数据存取
#include <stdarg.h>
void vaArgFun(int n, ...)
{
int i, temp;
va_list arg;
va_start(arg, n);//va_start系列函数其实是宏定义,此处根据栈上n参数位置,就可以让arg指向后面的一系列参数。
for(i = 0; i < n; i++)
{
temp = va_arg(arg, int);//根据参数类型取出参数
cout <<" vaArg = " << temp << endl;
}
va_end(arg);//恢复arg
}
//那样变长参数宏,就一样道理,下面测试最好用printf系列函数,好配合参数数量的变化
#define LOG(...){\
cout << "file = " << __FILE__;\
cout << __VA_ARGS__ << endl; \
}
//字符串
int UnicodeTest()
{
//分别用对应的长度和编码方式保存数据,但数据未必能够在终端显示出来
//utf8是变长的,多字节性的,与宽字节之前需要经常转码,才能兼容
//utf8 每个字占用的字节数量不定,所以不太适合固定偏移数组形式的取数据,代码上不太好写算法
char utf8[] = u8"\u4F60\u597D\u554A";
char16_t utf16[] = u"hello";
char32_t utf32[] = U"hello is \u4F60\u597D\u554A";
cout << utf8 <<"sizeof = " << sizeof(utf8)<< endl;
cout << utf16 <<"sizeof = "<< sizeof(utf16) << endl;
cout << utf32 <<"sizeof = "<< sizeof(utf32)<< endl;
//其中还包括一些不同字节码的转换操作,同时locale支持
//原生字符串支持
char u8string[] = u8R"((你好啊) “” \n 123)";//R代表原生, u8就是u8,u,U等字符宽度
cout << u8string << endl;
}
//noexcept,指定不抛出异常,它既是修饰符,也是操作符。
//一、类的析构函数默认是noexcept(true),不抛出异常,原因如下:
//如果析构函数本身的调用就是源自于某些其它异常的抛出,那么terminate函数将被自动调用,彻底终止你的程序。 同时如果异常抛出析构函数外了
//那析构后面的代码就无法执行了。
//作为操作符,它也可以用来更通用的模板编程
template <class T>
void fun() noexcept (noexcept(T())) {}//后面noexcept(T())是操作符,检测T()是否能够抛出异常。
//方便的就地初使化
class Init{
public:
Init(int m):m_i(m){cout << __func__ << endl;};//也可以初使化列表方式
~Init(){cout << __func__ << endl;};
//private:
int m_i = 3;//可以就地初使化
};
//非静态成员的sizeof
class People {
public:
int m_p;//可以通过类引用,进行sizeof取大小,同时必须是public的,感觉用处也不大
};
//friend 友元的改进,主要由friend class A 改变成了friend A,对应模板有用,如下例子:
template <typename T> class Pep {
friend T;//T作为了模板类的友元了,偶尔方便操作私有东西,提高性能和测试等
};
//修复符final override, 这个在JAVA里面经常见到,final就是代表对应函数不可被继续重载了,
//override就是子类说:我这是重载的,如果条件不满足,没有可重载的,就会报错了
//默认模板函数形参,与默认模板类形参
template<typename T1, typename T2 = int> class TClassTest;//从右开始才能行,因为左边可以省略
template<typename T1 = int, typename T2> TF(T1 a, T2 b){};//比较随意,因为实参决定
//extern template,声明一个已经特例化的模板,这样手动减少代码量,
extern template void externTFun<int>(int);
//强枚举类型,C的枚举类型一是容易名字空间污染,二是,其值会强制INT的转化,造成比较的时候一些错误,
//而强制枚举就是单纯的枚举空间集合进行比较
enum class StrongEnum:char {RED,BLUE,GREEN};
//对齐测试,
int alignasTest()
{
struct A {
int a;
char b;
};
cout << alignof(A) << endl;
struct alignas(16) B {
double r;
double g;
double b;
};//每个占用8个,后面对齐补上,16个字节对齐,
cout << alignof(B) << "sizeof = " << sizeof(B) << endl;
}
//继承构造函数,这个继承下来好像用处不大啊,节省一点书写?
class Dri_B : Init{
public:
Dri_B():Init(1){};
~Dri_B(){};
//using Init:Init;
};
#include <list>
#include <vector>
//调用自身其它构造函数的方法扩展:委派构造函数
class TDConstructed
{
template<class T> TDConstructed(T first, T last) : l(first, last){}
list<int> l;
public:
//就是调用其它构造函数的方式
TDConstructed(vector<int>& v): TDConstructed(v.begin(), v.end()){}
};
//右值引用:移动语义和完美转发. 这个就是一些函数,符值上的,针对非基本类型的坑,用这些东西来填的。减少COPY
int is_rvalue_reference_test()
{
cout << is_rvalue_reference<string &&> ::value << endl;
}
class HugeMem{
public:
HugeMem(int size): sz(size > 0 ? size : 1) {
c = new int[sz];
}
~HugeMem() { delete [] c; }
HugeMem(HugeMem && hm): sz(hm.sz), c(hm.c) {
hm.c = nullptr;
}
int * c;
int sz;
};
class Moveable{
public:
Moveable():i(new int(3)), h(1024) {}
~Moveable() { delete i; }
Moveable(Moveable && m)://需要一个右值引用构造函数的实现
i(m.i), h(move(m.h)) { // 强制转为右值,以调用移动构造函数
m.i = nullptr;
}
int* i;
HugeMem h;
};
//完美转发
void RunCode(int& a){cout << "RunCode lvalue" << endl;}
void RunCode(int&& a){cout << "RunCode rvalue" << endl;}
void RunCode(const int& a){cout << "const RunCode lvalue" << endl;}
void RunCode(const int&& a){cout << "const RunCode rvalue" << endl;}
template<typename T>
void PerfectForward(T&& t)//一、需要引用减少传参时候的临时对象创建。二、进行T类型的折叠,创造接受万能类型,传类型的引用,都完美传递。
{
//RunCode(static_cast<const int&&>(t));//ok
//RunCode(forward<T>(t));//传给实际调用的函数, forward 实际实现为:static_cast<T&&>
RunCode(static_cast<T>(t));//为什么不是static_cast<T>?实际测试来看,它能够传递左值引用 常量左值引用, 右值引用, 但不能传递常量右值引用
}
//追踪返回类型,对于不知道的返回类型,或者其它时候有用。如下这些例子:
auto pf1()->auto (*)()->int(*)(){
return nullptr;
}
template<class T>
auto Forward(T t)->decltype(t){//推导返回类型
return t;
}
//for语句改变,这些来自其它语言,以及for_each也类似之前模板
#include <algorithm>
int action1(int& e){e *= 2;}
void fortest()
{
int arr[19] = {1,3,4,9};
vector<int> v{10,3,1,2,1};
for_each(v.begin(), v.end(), action1);
for(auto& e:arr)
cout<<e<<endl;
}
//constexpr常量表达式,修饰下函数和值等,这样编译阶段就可以优化一下,少一些数据占用
//变长模板,包括变长模板类和模板函数,主要采用递归的方式进行定义和理解
//核心概念是定义时候的:参数包, 和 模板内部使用时候的: 包扩展
template<typename... E> class my_tuple;//声明其形式
template<typename H, typename... Tail>//...x代表参数包
class my_tuple<H, Tail...> : private my_tuple<Tail...>{};//x...代表包扩展,这里实现递归的主体关系
template<>class my_tuple<>{};//递归的边界条件,是参数取到数量为0个。
//模板函数的变长,理解方式一样,定义的时候也是主要找出其递归逻辑
//另外一种包扩展就是类似把B<A...>写成外部的B<A>... 这些展开方式是不一样的,不过相信可以理解
//原子类型等atomic_int,还有其它关于指令执行顺序的保证,还有线程相关等,属于基本概念,不做说明
//默认函数的控制,"=default" "=deleted",主要是针对那些默认生成的类函数,比如构造,析构等,进行删除和使用默认
//删除是不想别人使用它,比如COPY构造, 使用默认比如是因为规则上,那些默认生成的,可能你自定义了一个
//那剩下的那些可能都不会自动生成了,但有时候,你为了什么POD规则,保持纯性,还是使用默认的
//lambda函数,这里只给出一些基本的说明,后面函数式编程和模板编程上,再进入深入研究
//[capture](parameters) mutable->return-type
int lambda_test()
{
int a = 10, b = 11;
auto add = [=](int c)->int {return a + b + c;};//[=]捕捉列表的一种,捕捉父作用域内的变量值或者引用,或者this,后面分别是参数,返回值 ,函数体等
return add(9);
}
int main()
{
TestStruct test;
cout << "__func__ = " << test.name << endl;
//
vaArgFun(4, 1, 2, 3, 5);
LOG("log test");
//
UnicodeTest();
//编译器支持c++的版本
cout << "c++ version is " <<__cplusplus << endl;
//静态断言,这个需要编译时期可以确定的常量,可判定, 用处一般般.
//static_assert(sizeof(char) == sizeof(long), "type len is not equal !");
cout << sizeof(People::m_p) << endl;
cout << (StrongEnum::RED < StrongEnum::GREEN) <<endl;
//智能指针,跟平时自己封装的概念差不多
unique_ptr<Init> uPtr(new Init(4));
shared_ptr<Init> sPtr = make_shared<Init>(5);
weak_ptr<Init> wPtr = sPtr;
sPtr.reset();//显示释放
cout << wPtr.lock() << endl;//得到对象强指针
alignasTest();
is_rvalue_reference_test();
//
int lvalue = 33;
const int clvalue = 33;
PerfectForward(lvalue);
PerfectForward(clvalue);
PerfectForward(33);
PerfectForward(move(lvalue));
PerfectForward(move(clvalue));
RunCode(move(clvalue));
fortest();
return 0;
}