初始化列表
初始化列表为c++11新增的,可用于可变数量函数参数传参《不足之处是要求参数的数据类型一致》。
引入头文件:#include<initializer_list>
使用示例
double sum(initializer_list<double> i)
{
double sum = 0;
for (auto it = i.begin(); it != i.end(); it++)
{
sum += *it;
}
return sum;
}
int main()
{
//使用初始化列表
double total = sum({ 1,2,3.3,5,9.8 });
cout << total << endl;
}
运行结果:
委托构造
为了满足不同的需求,一个类中可能会重载多个构造函数。多个构造函数之间可能会有重复的代码,如成员变量初始化。委托构造就是在一个构造函数的初始化列表中调用另一个构造函数。
示例:
//委托构造
class A
{
private:
int a;
int b;
double c;
public:
A(double c_) : c(c_ + 3) {
cout << "A(double c_)" << endl;
}
A(int a_,int b_ ) : a(a_ + 1),b(b_+2) {
cout << "A(int a_,int b_)" << endl;
}
A(int a_, int b_, const string& str) : A(a,b) {
cout << "a=" << a << ", b=" << b << ", str=" << str << endl;
}
A(double c_, const string& str) : A(c) {
cout << "c=" << c <<", str=" << str << endl;
}
};
int main()
{
//委托构造
A a1(3, 5, "坤坤");
A a2(1.314, "荔枝");
}
运行结果:
注意:
(1)不要生成环装的构造过程(相互委托),容易造成死递归
(2)不要在初始化列表中列表中初始化其他的成员变量 (初始化只有一次,都委托出去了还整啥)
继承构造
c++11引入了继承构造,在派生类中使用using关键字来声明继承基类的构造函数。
示例:
/继承构造
class A
{
public :
int a;
int b;
A(int a_) :a(a_)
{
cout << "A(int a_)()" << endl;
}
A(int a_,int b_) :a(a_),b(b_)
{
cout << "A(int a_,int b_)()" << endl;
}
};
class B : public A
{
public:
double c;
using A::A;
B(int a_, int b_, double c_) : A(a, b), c(c_)
{
cout << "B(int a_, int b_, double c_)()" << endl;
}
};
int main()
{
//继承构造;
B b1(3);
B b2(4, 5);
B b3(6,7,1.314);
}
运行截图:此时类B就拥有了三个构造函数
移动语义
移动语义能够缓解深拷贝带来的没有意义的资源资源申请和释放(前提是被拷贝的对象的临时对象,拷贝完就没啥用了,若函数参数不加引用,移动语义就很有必要,因为都是临时对象)move()方法可将左值转换成右值
示例:
class A
{
public:
int* m = nullptr;
A() = default;
void alloc()
{
m = new int;
memset(m, 0, sizeof(int));
}
A(const A& a)
{
cout << "调用了拷贝构造函数!\n";
if (m == nullptr) alloc();
memcpy(m, a.m, sizeof(int));
}
//移动构造函数
A( A&& a)
{
cout << "调用了移动构造函数!\n";
if (m == nullptr) alloc();
m = a.m;
a.m = nullptr;
}
//移动赋值函数
A& operator=( A&& a)
{
cout << "调用了移动赋值函数\n";
if (this == &a) return *this;
if (m == nullptr) alloc();
m = a.m;
a.m = nullptr;
return *this;
}
A& operator=(const A& a)
{
cout << "调用了赋值函数\n";
if (this == &a) return *this;
if (m == nullptr) alloc();
memcpy(m, a.m, sizeof(int));
return *this;
}
};
int main()
{
A a1;
a1.alloc();
*(a1.m) = 4;
cout << *(a1.m) << endl;
A a2 =a1;
cout << *(a2.m) << endl;
A a3 =move(a1);
cout << *(a3.m) << endl;
auto f= []()
{
A a;
a.alloc();
*a.m = 5;
return a;
};
A a4;
a4= f();
cout << *(a4.m) << endl;
}
运行结果:
完美转发
在函数末班中,可以将自己的参数完美地转发给其他函数。所谓完美,不仅能准确地转发参数的值,还能保证被转发参数的左右值属性不变。
完美转发,决定了参数在传递过程中使用的是拷贝语义(调用拷贝构造函数)还是移动语义(调用移动构造函数)
使用方法:
(1)在模板中,函数的参数书写成T&&, 则该函数可以接受左值和右值
(2)提供模板函数:tsd::forward<T>(参数),用于转发参数
示例:
void func1(int& arg)
{
cout << "参数是左值:" << arg << endl;
}
void func1(int&& arg)
{
cout << "参数是右值:" << arg << endl;
}
template<typename T>
void func(T&& arg)
{
func1(forward<T>(arg));
}
int main()
{
int i = 1;
func(i);
func(2);
}
运行结果:
可变参数模板
c++11新特性,对参数进行泛化,能支持任意个数,任意数据类型的参数。
示例:
void print()
{
cout << "姬霓太美!" << endl;
}
template<typename T,typename ...Args>
void print(T arg, Args... args)
{
cout << "ikun你好:" << arg << endl;
cout <<"参数个数还有:"<< sizeof...(args) << endl;//参看参数个数,注意用法
print(args...);
}
int main()
{
//可变参数模板
print("鸡", "你", "太", "美");
}
时间操作
引入头文件#include <chrono>
时间长度
//时间长度
int main(){
chrono::hours t1(1);
chrono::minutes t2(60);
if (t1 == t2)
cout << "时间长度一致\n";
cout << t1.count() << endl;
cout << t2.count() << endl;
}
运行结果
计时器
注意:前面的chrono::steady_clock::time_point 可用auto自动推导类型,充分利用C++11的新特性
int main()
{
chrono::steady_clock::time_point start = chrono::steady_clock::now();
cout << "计时开始!\n";
chrono::steady_clock::time_point end = chrono::steady_clock::now();
auto dt = end - start;//单位是纳秒
cout << "耗用时间:" << dt.count() << "(纳秒),等于" << (double)dt.count() / (1000 * 1000 * 1000) << "秒\n";
}
运行结果:
多线程
创建线程
引入头文件:#include<thread>
最常用的构造方式:template< class function, class ... Args>
创建线程对象,在线程中执行任务函数fx的代码,args是要传递给任务的参数,fx可以是普通函数、类的非静态成员函数、类的静态成员函数、匿名函数、仿函数
示例:其中int main()是主线程,t1是子线程,最后调用join()函数释放线程资源,这里使用的是仿函数示例。
int main()
{
//多线程
thread t1([](int num, string str) {
for (int i = 0; i < 10; i++)
{
cout << "亲爱的坤哥" << num << "号" << ", " << str << endl;
Sleep(1000);//休眠1秒
}}, 3, "鸡你太美");
cout << "任务开始。\n";
for (int i = 0; i < 10; i++)
{
cout << "执行任务中!\n";
Sleep(1000);//休眠1秒
}
cout << "任务完成\n";
t1.join();//回收线程资源
}
运行结果:
回收线程资源
分为两种:join()函数:由主线程回收子线程资源
detach()函数:分离子线程,必须保证主线程的存活时间大于子线程
//多线程
thread t1([](int num, string str) {
for (int i = 0; i < 10; i++)
{
cout << "亲爱的坤哥" << num << "号" << ", " << str << endl;
Sleep(1000);//休眠1秒
}}, 3, "鸡你太美");
t1.detach();
this_thread命名空间
该命名空间包含一些成员函数,如:get_id(),sleep_for()、sleep_until()等
int main()
{
//多线程
thread t1([](int num, string str) {
cout << "仿函数子线程ID:" << this_thread::get_id() << endl;
for (int i = 0; i < 3; i++)
{
cout << "亲爱的坤哥" << num << "号" << ", " << str << endl;
//Sleep(1000);//休眠1秒
this_thread::sleep_for(chrono::seconds(1));//c++11休眠方式
}}, 3, "鸡你太美");
cout <<"main函数主线程ID:" << this_thread::get_id() << endl;
cout << "子线程ID:" << t1.get_id() << endl;
t1.join();//回收线程资源
}
运行结果:
call_once函数
在多线程环境中,某些函数只能被调用一次,例如:初始化某个对象
引入头文件:#include<mutex>
声明全局变量:once_flag flag; //本质是取值为0或1的锁
示例:
once_flag flag; //本质是取值为0或1的锁(全局变量)
void oncefun(string str)
{
cout << "only once: " << str << endl;
}
void func(const int & num,const string & str)
{
call_once(flag, oncefun, str);
for (int i = 0; i < 3; i++)
{
cout << "亲爱的坤哥" << num << "号" << ", " << str << endl;
//Sleep(1000);//休眠1秒
this_thread::sleep_for(chrono::seconds(1));//c++11休眠方式
}
}
int main()
{
thread t1(func, 1, "kun");
thread t2(func, 2, "man");
t1.join();
t2.join();
}
效果:在这里oncefun()函数只会被一个线程调用
包装器function
function模板类是一个通用的可调用对象的包装器,用简单的、统一的方式处理可调用对象
引入头文件:#include<functional>
下面给出针对普通函数和类的非静态成员函数的包装使用
//普通函数
void show(int num, const string& str)
{
cout << "亲爱的" << num << "号ikun: " << str<<"\n";
}
//类的非静态成员函数
struct A
{
void show(int num, const string& str)
{
cout << "亲爱的" << num << "号ikun: " << str << "\n";
}
};
int main()
{
void(*fp1)(int, const string&) = show;//函数指针方式
fp1(1, "香精煎鱼");
function<void(int, const string&)> fn1 = show; //包装普通函数
fn1(1, "香精煎鱼");
A a;
void (A:: * fp2)(int, const string&) = &A::show;//函数指针方式
(a.*fp2)(2, "魅力猫");
function<void(A&,int, const string&)> fn2 = &A::show;//包装类的非静态成员函数
fn2(a,2, "魅力猫");
}
测试结果:
blind()函数
blind()函数模板被称为函数适配器(绑定器),它用一个可调用对象及其参数,生成一个新的可调用对象,以适应模板。
引入头文件:#include<functional>
使用示例:
普通函数:
function<void(int, const string&)> fbn1 =bind(show,placeholders::_1, placeholders::_2);
fbn1(1, "香精煎鱼");
类的非静态成员函数:
function<void(A&, int, const string&)> fbn2 = bind(&A::show, placeholders::_1, placeholders::_2, placeholders::_3);
fbn2(a, 2, "魅力猫");
绑定器和包装器的应用
应用一:可变函数和参数
示例:
void show0()
{
cout << "亲爱的ikun" << "\n";
}
void show1(const string& str)
{
cout << "亲爱的ikun: " << str << "\n";
}
void show2(int num, const string& str)
{
cout << "亲爱的" << num << "号ikun: " << str << "\n";
}
//使用完美转发、该函数功能类似thread()构造函数,其中使用绑定器和构造器
template<typename Fn, typename...Args>
auto show(Fn &&fn, Args&&...args)-> decltype(bind(forward<Fn>(fn), forward<Args>(args)...))
{
cout << "表白开始" << endl;
auto f = bind(forward<Fn>(fn), forward<Args>(args)...);
f();
cout << "表白结束" << endl;
return f;
}
int main()
{
show(show0);
show(show1, "基尼太美");
show(show2, 3, "荀坤");
}
运行效果:
应用二:取代虚函数
C++虚函数在执行过程中会跳转两次(先查找对象的函数表,再次通过该函数表中的地址找到真正的执行地址,这样,cpu会跳转两次,而普通函数值跳转一次)CPU每跳转一次,预取指令要作废很多,所以效率会很低。为了管理方便(基类指针可指向派生类对象和自动析构派生类),保留类之间的继承关系。
示例:
struct Hero {
//virtual void show() { cout << "释放技能\n"; }
function<void()> m_callback; //用于绑定子类的成员函数
//注册子类成员函数,之类成员函数没有参数
template<typename Fn,typename... Args>
void callback(Fn&& fn, Args&&...args)
{
m_callback = bind(forward<Fn>(fn), forward<Args>(args)...);
}
void show()
{
m_callback();
}
};
struct gmwz: public Hero {
void show() { cout << "宫本武藏释放技能\n"; }
};
struct lb: public Hero {
void show() { cout << "李白释放技能\n"; }
};
int main()
{
int id;
cout << "请输入英雄编号:";
cin >> id;
Hero* pt=nullptr;
if (id == 1)
{
pt = new gmwz;
pt->callback(&gmwz::show, static_cast<gmwz*>(pt)); //注册回调函数
}
if (id == 2)
{
pt = new lb;
pt->callback(&lb::show, static_cast<lb*>(pt));//注册回调函数
}
pt->show();
delete pt;
}
运行效果: