c++总结(2)

1、initializer_list

auto i = {1, 2, 3, 4};
cout << typeid(i).name()<<endl;

//输出
//class std::initializer_list<int>

c++11中的vector<int> vec{1,2,3};就是使用了initializer_list,可变参数也是使用的initializer_listinitializer_list最常用的方式是通过大括号包围的值列表对其进行初始化:除了不能修改vlist中的值以外,可以像一般的list一样使用。

vector<int>和initializer_list的区别

  • vector有push_back操作,其有reserve重新new内存空间,是在堆中进行的操作,而initializer_list是在栈上,其内部只有begin和end函数,函数内不能修改这些数值
  • vector会发生拷贝,而initializer_list是指针语义,里面的元素不会被拷贝

2、map

  •  map没有赋值的时候是被初始化为0的
	std::map<int, int> m;
	int a = m[1110];
    
    //输出 a为0
  •  map自定义键值类型

map底层的实现是红黑树,红黑树具有二叉搜索树的性质,map在插入的时候具有对key进行排序的功能,这是因为c++的基本类型有默认的比较方式,即便是string类型,其可以自动排序那是因为string内部实现了operator < 的重载。

        类或结构体中重载operator<

#include <iostream>
#include <map>
#include <string>
using namespace std;

class Person {
public:
	string name;
	int age;

	Person(string n, int a) 
	{
		name = n;
		age = a;
	}

    //重载operator<
	bool operator<(const Person &p) const //注意这里的两个const
	{
		return (age < p.age) || (age == p.age && name.length() < p.name.length());
	}
};

int main() 
{
	map<Person, int> group;
	group[Person("David", 27)] = 2;
	group[Person("Eric", 18)] = 3;
	for (auto iter = group.begin(); iter != group.end(); iter++)
	{
		cout << iter->first.name
			<< " " << iter->first.age
			<< " : " << iter->second
			<< endl;
	}

	return 0;
}

3、为什么析构函数是虚函数

基类的指针指向派生类的时候要将基类的析构函数设置为虚函数,这样是为了防止派生类无法析构导致内存泄露

  • 基类析构未使用虚函数
#include<iostream>
using namespace std;
class Base {
public:
	Base() {};
	~Base() { cout << "Output from the destructor of class Base!" << endl; };

	void DoSomething() { cout << "Do something in class Base!" << endl; };
};

class Derived : public Base {
public:
	Derived() {};
	~Derived() { cout << "Output from the destructor of class Derived!" << endl; };

	void DoSomething() { cout << "Do something in class Derived!" << endl; }
};
int main() 
{
	Base *p = new Derived;
	p->DoSomething();
	delete p;
	return 0;
}

上述代码,未使用virtual,输出的结果没有进入派生类的析构函数中,会导致内存泄露的发生。

  •  基类析构函数使用虚函数
#include<iostream>
using namespace std;
class Base {
public:
	Base() {};
	virtual ~Base() { cout << "Output from the destructor of class Base!" << endl; };

	void DoSomething() { cout << "Do something in class Base!" << endl; };
};

class Derived : public Base {
public:
	Derived() {};
	~Derived() { cout << "Output from the destructor of class Derived!" << endl; };

	void DoSomething() { cout << "Do something in class Derived!" << endl; }
};
int main() 
{
	Base *p = new Derived;
	p->DoSomething();
	delete p;
	return 0;
}

根据多态的性质,在释放的时候就是先析构派生类,然后在析构基类

4、static介绍

  1. 静态资源是类初始化的时候加载的,
  2. 而非静态资源是类实例化对象的时候加载的,
  3. 类的初始化早于类实例化对象 
  •  静态成员函数属于整个类,在类实例化之前就已经分配空间了。而非静态成员必须在类实例化之后才能有内存空间。
#include <iostream>
using namespace std;
class Point
{
public:
	void init()
	{
	}
	static void output()
	{
        //error  报非静态成员引用必须与特定对象对应,即静态成员函数不能调用非静态变量
		cout << m_x << endl;    
	}

	static void output1()
	{
        //ok  没有非静态成员就可以
		cout << “hello”<< endl;    
	}
private:
	int m_x;
};
void main()
{
	Point pt;
	pt.output();
}
  • 非静态成员函数可以调用静态变量,前提是静态变量要提前在类外初始化
#include <stdio.h>
class Point
{
public:
	Point()
	{
		m_nPointCount++;
	}
	~Point()
	{
		m_nPointCount--;
	}
	static void output()
	{
		printf("%d\n", m_nPointCount);
	}
private:
	static int m_nPointCount;
	int m_1;
};
int Point::m_nPointCount = 0;    //初始化则ok,否则不ok
void main()
{
	Point pt;
	pt.output();
}

5、私有构造函数

  • 将构造函数私有化之后就不能在类的外部构造对象了,也不能在外部构造子类的对象了。但是可以通过一个public的static静态函数来访问类中定义的函数和静态成员,单例模式就是这样。
  • 这样的好处就是可以阻止用户在类外调用析构该对象了。
#include<iostream>
using namespace std;

class Singleton
{
private:
	Singleton()
	{
		cout << "Singleton" << endl;
	}

public:
	static Singleton* getInstance()
	{
		if (m_instance == nullptr)
			m_instance = new Singleton;
		return m_instance;
	}

    //析构函数中释放,delete是不对的,析构函数中的delete会再次进入析构,导致循环调用了
	~Singleton()
	{
		//if (m_instance)
		//{
		//	cout << "~Singleton" << endl;
		//	delete m_instance;
		//	m_instance = nullptr;		
		//}
	}
        
    //释放
	void Release()
	{
		if (m_instance)
		{
			cout << "~Singleton" << endl;
			delete m_instance;
			m_instance = nullptr;
		}
	}
private:
	static Singleton *m_instance;
};

Singleton *Singleton::m_instance = nullptr;

int main()
{
	Singleton* m1 = Singleton::getInstance();
	Singleton* m2 = Singleton::getInstance();
	m1->Release();

    //error
    //delete m1;
    //m1 = nullptr;
}

也可以通过在类内部定义一个静态成员变量来释放单例的内存,静态成员变量会在程序结束的时候自动进入析构释放资源。 所以该静态成员变量是需要是一个实现析构的类。

程序在结束的时候,系统会自动析构所有的全局变量和所有的类的静态成员变量。利用这个特征,我们可以在单例类中定义一个静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例

#include<iostream>
using namespace std;

class Singleton
{
private:
	Singleton()
	{
		cout << "Singleton" << endl;
	}

public:
	static Singleton* getInstance()
	{
		if (m_instance == nullptr)
			m_instance = new Singleton;
		return m_instance;
	}

	~Singleton()
	{
		//if (m_instance)
		//{
		//	cout << "~Singleton" << endl;
		//	delete m_instance;
		//	m_instance = nullptr;		
		//}
	}

	void Release()
	{
		if (m_instance)
		{
			cout << "~Singleton" << endl;
			delete m_instance;
			m_instance = nullptr;
		}
	}

	/*
	程序在结束的时候,系统会自动析构所有的全局变量和所有的类的静态成员变量。
	利用这个特征,我们可以在单例类中定义一个静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。
	*/
	class CRelease
	{
	public:
		~CRelease()
		{
			if (m_instance)
			{
				cout << "~Singleton" << endl;
				delete m_instance;
				m_instance = nullptr;
			}
		}
	private:
		static CRelease m_re;
	};
private:
	static Singleton *m_instance;
	
};

Singleton *Singleton::m_instance = nullptr;
Singleton::CRelease mRelease;	//静态变量需要初始化,要不然进不去析构函数
int main()
{
	Singleton* m1 = Singleton::getInstance();
	Singleton* m2 = Singleton::getInstance();
	//m1->Release();
}

 shared_ptr并自定义deleter

#include <iostream>
#include <mutex>
using namespace std;

mutex mtx;

class Singleton
{
public:
	static shared_ptr<Singleton> getInstance()
	{
		//双重检查,避免单重检查每次都加锁
		if (m_instance == nullptr)
		{
			lock_guard<mutex> lck(mtx);
			if(m_instance == nullptr)
				m_instance = make_shared<Singleton>();
		}
			
		return m_instance;
	}
private:
	static void Release(Singleton *s)
	{
		cout << "Release" << endl;
		delete s;
	}

private:
	static shared_ptr<Singleton> m_instance;
};

shared_ptr<Singleton> Singleton::m_instance(nullptr, Singleton::Release);

int main()
{
	shared_ptr<Singleton> m1 = Singleton::getInstance();
	shared_ptr<Singleton> m2 = Singleton::getInstance();
}

6、thread的拷贝、转移问题

c++11中的thread不能被拷贝,其内部没有拷贝构造函数,thread的源码如下:

    thread(thread&& _Other) noexcept
		: _Thr(_Other._Thr)
		{	// move from _Other
		_Thr_set_null(_Other._Thr);
		}

	thread& operator=(thread&& _Other) noexcept
		{	// move from _Other
		return (_Move_thread(_Other));
		}

	thread(const thread&) = delete;
	thread& operator=(const thread&) = delete;

将thread传递的时候要使用右值引用和move函数

#include <thread>
#include <iostream>

using namespace std;

class CThread
{
public:
	CThread(thread &th):m_th(move(th))
	{
		cout << "\nCThread, id:" << m_th.get_id() << \
			", joinable status is:" << m_th.joinable() << endl;
	}

	~CThread()
	{
		cout << "~CThread" << endl;
		m_th.join();
	}

	CThread(const CThread &th) = delete;
	CThread &operator=(const CThread &th) = delete;

private:
	thread m_th;
};

void func()
{
	cout << "func" << endl;
}
int main()
{
	thread t(func);
	cout << "thread before move, id:" <<t.get_id()<<	\
		", joinable status is:"<< t.joinable()<<endl;
	CThread th(t);

	cout << "\nthread after move, id:" << t.get_id() << \
		", joinable status is:" << t.joinable() << endl;
}

7、lambda表达式作为函数参数

  •  模板参数

        编译器根据传参类型自动推断出T的类型,然后运行

#include <iostream>

template <typename T>
void print(const T &func)
{
	std::cout << typeid(func).name() << std::endl;
	std::cout << "print " << std::endl;
	func();
}

int main()
{
	int val = 10;
	auto lambda_func = [&]() {
		std::cout << "lambda : " << val << std::endl;
	};
	print(lambda_func);
}

  •  function函数
#include <iostream>
#include <functional>


void output(std::function<void(int)> &func)
{
	std::cout << "output " << std::endl;
	func(20);
}

int main()
{
	int val = 10;
    
    //必须写成显式的std::function<void(int)>,而不能使用auto
	std::function<void(int)> func = [&](int x) {
		std::cout << "x : " << x <<", val: "<<val<< std::endl;
	};

	output(func);
}

  •  lambda作为线程函数
#include <iostream>
#include <thread>

int main()
{
	int val = 10;
	auto func = [&](int x) {
		std::cout << "x : " << x <<", val: "<<val<< std::endl;
	};

	std::thread t(func, 200);
	if (t.joinable())
		t.join();
}

8、thread和async的比较

 async用来创建异步任务,相比于thread其有三种不同的创建策略,并且其通过future来保存返回结果

template<class _Fty,
	class... _ArgTypes>
	_NODISCARD inline future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>>
		async(launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args)

参数介绍
_Policy:
    1.std::launch::async 异步启动,在调用std::async()时创建一个新的线程以异步调用函数,并返回        future对象;
    2.std::launch::deffered 延迟启动,在调用std::async()时不创建线程,直到调用了future对象的get()或wait()方法时,才创建线程;
    3.std::launch::async||std::launch::deffered 默认策略,由系统决定怎么调用

_Fnarg:函数指针(函数指针,函数对象,lambda表达式)
_Args:函数参数列表

#include <future>
#include <iostream>


int main()
{
	auto lam_func = [](int x) {
		std::this_thread::sleep_for(std::chrono::microseconds(500));
		std::cout << "val = " << x << std::endl;
	};
	std::future<void> f1 = std::async(std::launch::async, lam_func, 10);
}

 std::launch::deffered的时候在调用wait的时候才开始执行线程操作

#include <future>
#include <iostream>


int main()
{
	int sum = 10;
	auto lam_func = [&](int x) {
		std::this_thread::sleep_for(std::chrono::microseconds(500));
		sum += x;
		std::cout << "val = " << x <<", sum = "<<sum<< std::endl;
		return sum;
	};
	std::future<int> f1 = std::async(std::launch::deferred, lam_func, 10);
	f1.wait();
	auto ret = f1.get();
	std::cout << "ret = " << ret << std::endl;
}

 future类成员函数

  • get:当与该 std::future 对象相关联的共享状态标志变为 ready 后,调用该函数将返回保存在共享状态中的,如果共享状态的标志不为 ready,则调用该函数会阻塞当前的调用者
  • wait:等待与当前std::future 对象相关联的共享状态的标志变为 ready如果共享状态的标志不是 ready,调用该函数会被阻塞当前线程,直到共享状态的标志变为 ready一旦共享状态的标志变为 ready,wait() 函数返回,当前线程被解除阻塞
  • wait_for:和wait函数类似,需要设置一个时间段rel_time,如果共享状态的标志在时间段结束之前没有被Provider设置为valid,则调用wait_for的线程被堵塞,在等待了rel_time时间后,wait_for函数返回
  • wait_until:和wait函数类似,需要设置一个系统绝对时间点abs_time,如果共享状态的标志在该时间点到来之前没有被 Provider 设置为 ready,则调用 wait_until 的线程被阻塞,在 abs_time 这一时刻到来之后 wait_until() 返回

wait_for以及wait_until返回值类型

  • future_status::ready:共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常
  • future_status::timemout:超时,即在规定的时间内共享状态的标志没有变为 ready
  • future_status::deferred:共享状态包含一个 deferred 函数。如在saync中第一个参数指定为std::launch::deferred

#include <future>
#include <iostream>


int main()
{
	int sum = 10;
	auto lam_func = [&](int x) {
		std::this_thread::sleep_for(std::chrono::microseconds(50));
		sum += x;
		std::cout << "val = " << x <<", sum = "<<sum<< std::endl;
		return sum;
	};
	std::future<int> f1 = std::async(std::launch::async, lam_func, 10);

	if (f1.wait_for(std::chrono::microseconds(0)) == std::future_status::ready)
	{
		std::cout << "ready" << std::endl;
	}
	else if (f1.wait_for(std::chrono::microseconds(0)) == std::future_status::timeout)
	{
		std::cout << "timeout" << std::endl;
	}
	else
	{
		std::cout << "defer" << std::endl;
		f1.get();
	}
}

thread的函数形式和async一样。重点说一下async和thread的区别

  • std::thread:而thread是必定创建线程。当然系统资源紧张也会可能创建失败
  • std:: async:主要是用于创建一个异步任务,当系统资源紧张时有可能不创建线程或者创建线程失败。并且当枚举宏为std::launch::defered时,肯定不会创建线程并且只有调用get或者wait才开始执行异步任务
  • std::async函数模板更加容易拿到线程函数的返回值,所以更想要拿返回值的,可以使用std::async
  • async不是线程池,但是其内部可以重复使用开辟的线程,而thread不行
#include <atomic>
#include <vector>
#include <future>
#include <thread>
#include <unordered_set>
#include <iostream>

int main()
{
	std::atomic<int> someCount = 0;
	const int THREADS = 2000;
	std::vector<std::thread> threadVec(THREADS);
	std::vector<std::future<void>> futureVec(THREADS);

	std::unordered_set<std::thread::id> uniqueThreadIdsAsync;
	std::unordered_set<std::thread::id> uniqueThreadsIdsThreads;
	std::mutex mutex;

	auto lam = [&](bool isAsync)
	{
		for (int i = 0; i < 1000000; ++i)
			someCount++;

		auto threadId = std::this_thread::get_id();
		if (isAsync)
		{
			std::lock_guard<std::mutex> lg(mutex);
			uniqueThreadIdsAsync.insert(threadId);
		}
		else
		{
			std::lock_guard<std::mutex> lg(mutex);
			uniqueThreadsIdsThreads.insert(threadId);
		}
	};

	for (int i = 0; i < THREADS; ++i)
		threadVec[i] = std::thread(lam, false);

	for (int i = 0; i < THREADS; ++i)
		threadVec[i].join();
	std::cout << "Number of threads used running std::threads = " << uniqueThreadsIdsThreads.size() << std::endl;

	for (int i = 0; i < THREADS; ++i)
		futureVec[i] = std::async(lam, true);
	for (int i = 0; i < THREADS; ++i)
		futureVec[i].get();
	std::cout << "Number of threads used to run std::async = " << uniqueThreadIdsAsync.size() << std::endl;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值