C++之奇妙特性

本文介绍了C++11的一些重要特性,包括初始化列表用于函数参数,委托构造减少代码重复,继承构造简化派生类构造,移动语义优化资源管理,完美转发保持参数属性,可变参数模板实现泛化函数,以及多线程的相关概念和使用方法。
摘要由CSDN通过智能技术生成

初始化列表

初始化列表为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;
}

运行效果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值