1.委托构造函数允许使用类中的某一构造函数调用同类中的其它构造函数。
#include <iostream>
using namespace std;
class Test
{
public:
Test() {};
Test(int max)
{
this->m_max = max > 0 ? max : 100;
}
Test(int max, int min):Test(max)
{
this->m_min = min > 0 && min < max ? min : 1;
}
Test(int max, int min, int mid):Test(max, min)
{
this->m_middle = mid < max && mid > min ? mid : 50;
}
int m_min;
int m_max;
int m_middle;
};
int main()
{
Test t(90, 30, 60);
cout << "min: " << t.m_min << ", middle: "
<< t.m_middle << ", max: " << t.m_max << endl;
return 0;
}
2.继承构造函数可以让派生类直接使用基类的构造函数,而无需自己再写构造函数:通过使用 using 类名::构造函数名 来声明使用基类的构造函数,这样子类中就可以不定义相同的构造函数了,直接使用基类的构造函数来构造派生类对象。
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
Base(int i) :m_i(i) {}
Base(int i, double j) :m_i(i), m_j(j) {}
Base(int i, double j, string k) :m_i(i), m_j(j), m_k(k) {}
int m_i;
double m_j;
string m_k;
};
class Child : public Base
{
public:
using Base::Base;
};
int main()
{
Child c1(520, 13.14);
cout << "int: " << c1.m_i << ", double: " << c1.m_j << endl;
Child c2(520, 13.14, "i love you");
cout << "int: " << c2.m_i << ", double: "
<< c2.m_j << ", string: " << c2.m_k << endl;
return 0;
}
3.(1)std::function是可调用对象的包装器,本身是模板类。使用可调用对象对类进行实例化得到一个对象后,就可以通过这个对象完成对包装函数的调用了。std::bind用来将可调用对象与其参数一起进行绑定成为仿函数,这个仿函数去实例化std::function得到对象。
具体的可以参考std::functiona,其中有必要说一下的是对于类成员变量的包装。
std::function<int(A&)> A_Value = &A::m_Value;
//调用
A m_a;
A_Value(m_a);
(2)另外,std::function引入的必要是啥呢?可以看出,std::function做到了所有可调用对象的统一,在函数回调中避免了各种回调对象的杂乱无章。回调函数就是在两个独立函数或者独立类通信的通道,在此也分享一个回调函数的例子:
#include <iostream>
#include <vector>
class CallBack {
public:
virtual void GetMessage(int) const = 0;
};
class Worker {
private:
CallBack* callbackptr;
public:
Worker(CallBack* callbackptr_) :callbackptr(callbackptr_) {};
void ReturnNum(std::vector<int> v)
{
int num = 0;
for (auto iter = v.begin(); iter != v.end(); iter++)
{
num += *iter;
}
if (callbackptr != NULL)
callbackptr->GetMessage(num);
}
};
class Boss :public CallBack {
private:
Worker* workerptr;
std::vector<int> v;
public:
Boss(std::vector<int> v_) :v(std::move(v_))
{
workerptr = new Worker(this);
}
void CallWorker()
{
workerptr->ReturnNum(std::move(v));
}
void GetMessage(int num) const final
{
std::cout << "The Message is:" << num << std::endl;
}
};
int main()
{
std::vector<int> v = { 1,2,3,4,5 };
Boss boss(std::move(v));
boss.CallWorker();
}
4.右值引用为移动语义而生,移动语义是为了解决结构或类中函数指针成员需要深层拷贝的效率问题的,所以对于没有指针成员的类或结构体来说,移动语义没什么鸟用。为什么要特别引入“移动”?“移动”(move)相比复制(copy)多一个前提条件:源对象活不长了,它所拥有的东西马上就要被销毁。因为这个前提条件,就可以直接把源对象所拥有的东西直接拿来交给目标对象继续使用,不用担心源对象突然还魂又要用它的东西了。
(1)移动:
源对象:这本书我不要了,谁要用拿走。
目标对象:我要,你给我吧,既然你不用我就直接拿走了。
(2)复制:
目标对象:我想用你这本书。
源对象:可我有可能还用,这样吧,你去把这本书打印一份然后把复制本拿走。
目标对象:好的,真麻烦。
5.auto自动类型推导,当变量不是指针或者引用类型时,推导的结果中不会保留 CV(const、volatile) 关键字;当变量是指针或者引用类型时,推导的结果中会保留 CV(const、volatile )关键字。
6.(1)在T&&与auto&&这两种情况下,&& 被称作未定的引用类型,在进行类型推导时,右值引用类型(&&)会发生变化,这种变化被称为引用折叠,规则如下:通过右值推导 T&& 或者 auto&& 得到的是一个右值引用类型;通过非右值(右值引用、左值、左值引用、常量右值引用、常量左值引用)推导 T&& 或者 auto&& 得到的是一个左值引用类型。
int&& a1 = 5;//a1为右值引用
const int& s1 = 100;
auto&& dd = s1;//dd为常量左值引用类型.
(2)std::forward< T >(t)中,当T为左值引用类型时,t将被转换为T类型的左值;当T不是左值引用类型时,t将被转换为T类型的右值。
template<typename T>
void testForward(T && v)
{
printValue(forward<T>(v));
cout << endl;
}
int main()
{
testForward(520);//520是右值,推断T是int,ParamType(T&&)是int &&即为右值引用,forward<T>(v)为右值,因为T不是左值引用
int num = 1314;
testForward(num);//num是左值,T和ParamType都是int &即为左值引用,forward<T>(v)为左值,因为T是左值引用
testForward(forward<int>(num));//forward<int>(num)为右值,故和520效果一样,forward<T>(v)为右值,因为T不是左值引用
testForward(forward<int&>(num));//forward<int&>(num)为左值,故和num效果一样,forward<T>(v)为左值,因为T是左值引用
testForward(forward<int&&>(num));//forward<int>(num)为右值,故和520效果一样,forward<T>(v)为右值,因为T不是左值引用
return 0;
}
7.shared_ptr可以通过各种方式进行初始化,值得一说的是可以自己指定删除器,删除器函数本质是一个回调函数,只需要把函数实现,其调用是由智能指针完成的,另外,std::shared_ptr的默认删除器不支持数组对象。
int main()
{
shared_ptr<int> ptr(new int(250), [](int* p) {delete p; });
return 0;
}
8.变量只读和常量不是同一个概念!二者两种都可以通过const修饰来完成。例如const int& b = a1;这句代码,b被const修饰表示它是只读的,因此不能去写b,但是a1的值变了b的值自然是跟随变化的,因此说b不是一个常量,而是一个变量且是一个只读的变量。
void func(const int num)
{
const int count = 24;
int array[num]; // error,num是一个只读变量,不是常量
int array1[count]; //正确,因为count是一个常量。
}
9.constexpr,这个关键字是用来修饰常量表达式的。所谓常量表达式,指的就是由多个(≥1)常量(值不会改变)组成并且在编译过程中就得到计算结果的表达式,黑体部分也是这个关键字存在的意义。
10. chrono库
(1)std::chrono::duration表示时间间隔,是一个模板类,第一个模板参数指定周期数目的类型,例如整型或浮点型,第二个模板参数指定周期。其中这个周期也是一个模板类,第一个参数是分子,第二个参数是分母(默认是1),得到的分数值就是一个周期的秒数,而且两个模板参数都属于非类型参数。下面的代码代表有6个周期,且一个周期为1/1000秒即1毫秒,故时间周期为6毫秒。
std::chrono::duration<int, std::ratio<1, 1000>> ms_(6);
(2)获取当前时间使用system_clock,计算程序耗时使用stead_clock。
std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
std::chrono::system_clock::time_point point = std::chrono::system_clock::now();
time_t point_t = std::chrono::system_clock::to_time_t(point);
std::cout << ctime(&point_t) << std::endl;
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
std::cout << (std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1)).count() <<"ms"<< std::endl;
11.前向声明
(1)可以声明一个类而不定义它。这个声明,有时候被称为前向声明(forward declaration)。在声明之后,定义之前,类Screen是一个不完全类型(incompete type),即已知Screen是一个类型,但不知道包含哪些成员。不完全类型只能以有限方式使用,不能定义该类型的对象,不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。
(2)它的存在意义是什么呢?不必要的#include,会增加编译时间。因此,如果只是在上述情况下使用某一个类的的话,使用前向声明即可而不必使用#include。
12.可变模板参数
(1)用于模板函数。如下代码所示,在模板声明中、函数形参中、使用可变参数中写法都是不同的,下面是运行结果
void print() {};
template<typename Head,typename ...T>
void print(Head head, T...args)
{
std::cout << "Head:" << head << std::endl;
std::cout << "Head_Num:" << sizeof...(args) << std::endl;
print(args...);
}
int main()
{
print(123, "hell0", 3.14);
}
Head:123
Head_Num:2
Head:hell0
Head_Num:1
Head:3.14
Head_Num:0
(2)用于模板类
template<typename... Values> class tuple;
template<> class tuple<> {};
template<typename Head, typename... Tail>
class tuple<Head, Tail...>
:private tuple<Tail...>
{
typedef tuple<Tail...> inherited;
protected:
Head m_head;
public:
tuple() {};
tuple(Head v, Tail... vtail)
:m_head(v), inherited(vtail...) {};
Head head() { return m_head; }
inherited& tail(){ return *this; }
};
int main()
{
tuple<int, double, std::string> tup(1, 1.2, "Hello");
std::cout << tup.head() << std::endl;
auto tup1 = tup.tail();
std::cout << "endl" << std::endl;
}