嵌入式c++(十)

一 可变参数

1.1 c语言的可变参数

实现原理:参数个数不确定,但是所有参数保存在栈上是连续的。
#include <iostream>
#include <stdarg.h>
using namespace std;
//实现可变参数的条件:函数的参数一定要由特殊的作用(标明后面参数的个数或者类型)
int func(int num1, int num2, ...)  //第一个参数num1是参数的个数或者参数的类型及个数
{
	va_list v1;  //va_list指针,用于va_start可变参数,为char*
	va_start(v1,num1);  //是v1指向后面的可变参数
	printf("v1 = %d\n",*v1);

	int res;
	for (int i = 0; i < num1 ; i++)
	{
		res = va_arg(v1, int); //先返回当前元素的值,再把v1往后跳4个字节(sizeof(int)的大小),使得指向下一个参数
		printf("res = %d\n",res);
	}
	va_end(v1);  //释放指针
	return 0;
}
int main()
{
	func(3, 4, 5, 6);
}

1.2 c++11中的可变参数

不建议使用c语言的可变参数,因为内部原型检查机制不全,不安全
initializer_list 列表初始化

1.3 initializer_list 列表初始化

1.3.1 本质

用于表示某种特定类型的值的数组,和vector一样,initializer_list 也是一种模板类型

1.3.2 使用

用{}来初始化变量  比如:std::vector<int> a{1,2,3,4,5};

1.3.3 初始化区别

int c = 3.3;  // 默认类型转换
int b = (int){3.3}  //编译器会给出警告(也有可能是错误)

1.3.4 使用

#include <iostream>
#include <stdarg.h>
using namespace std;
class A
{
public:
	A(const initializer_list<int> &t)
	{
		for (auto tmp : t)  //范围for
		{
			cout << tmp << endl;
		}
	}
};
template <typename T>
void func(initializer_list<T> arg)
{
	auto it = arg.begin();
	for (; it != arg.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
int main(int argc,char *argv[])
{

	
	initializer_list<int> init_list{ 1,2,3,4,5,6 };
	//cout << init_list << endl;
	//cout << init_list[0] << endl;
	auto it = init_list.begin();
	for (; it != init_list.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	func({ 1,2,3,4,5,6,7,8,8,9});
	A a{1,2,3,4,5};

	//int a = { 3.3 };
	initializer_list<double> aa = { 3.3 };
	cout << endl;
	
}

二 萃取技术

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

template <typename T>
void printTraitsInfo(const T& t)
{
	cout << "--------------begin-----------" << endl;

	cout << "我们要萃取的类型名字是:" << typeid(T).name() << endl;

	cout << "is_void = " << is_void<T>::value << endl;                                    //类型是否是void
	cout << "is_class = " << is_class<T>::value << endl;                                  //类型是否是一个class
	cout << "is_object = " << is_object<T>::value << endl;                                //类型是否是 一个对象类型
	cout << "is_pod = " << is_pod<T>::value << endl;                                      //是否普通类(只包含成员变量,不包含成员函数);   POD(plain old data)
	cout << "is_default_constructible = " << is_default_constructible<T>::value << endl;  //是否有缺省构造函数
	cout << "is_copy_constructible = " << is_copy_constructible<T>::value << endl;        //是否有拷贝构造函数
	cout << "is_move_constructible = " << is_move_constructible<T>::value << endl;        //是否有移动构造函数
	cout << "is_destructible = " << is_destructible<T>::value << endl;                    //是否有析构函数
	cout << "is_polymorphic = " << is_polymorphic<T>::value << endl;                      //是否含有虚函数
	cout << "is_trivially_default_constructible = " << is_trivially_default_constructible<T>::value << endl;      //缺省拷贝构造函数是否是可有可无的(没有也行的),返回1表示确实可有可无

	cout << "has_virtual_destructor = " << has_virtual_destructor<T>::value << endl;      //是否有虚析构函数

	cout << "--------------end-------------" << endl;

}

class A
{
public:
	A() = default;
	A(A&& ta) = delete;           //移动构造:你要不写delete,系统一般就会认为你有这个成员函数;
	A(const A& ta) = delete;      //拷贝构造 
	virtual ~A() {}
};
class B
{
public:
	int m_i;
	int m_j;
};
class C
{
public:
	C(int t) {} //有自己的构造函数,编译器不会给你提供缺省构造函数
};

void func()
{
	printTraitsInfo(int());     //扔一个临时对象进去
	printTraitsInfo(string());
	printTraitsInfo(A());
	printTraitsInfo(B());
	printTraitsInfo(C(1));
	printTraitsInfo(list<int>());
}


int main(void)
{
	func();
}

三 STL

3.1 stl的概念

为了复用性的提升
为了建立数据结构和算法的一套标准,并且降低其间的耦合关系,以及提升各自的独立性(高内聚),交互性操作(相互合作性),c++社群诞生了STL

3.2 价值

低层次:一个非常实用的零部件,以及以一个整合的组织去使用。
高层次:以泛型思维为基础,系统化,条例清晰的:“软件组织分类学”。

3.3 历史

STL  1979 c++ 1971年
1987年 Ada libray c++还未提出template
1992 帕罗奥图 Alex项目-->Stl
1993年 贝尔 ANSI/ISOC++

3.4 STL的六大组件

(1)容器

各种数据结构,vector,list,deque,set,map,用来存放数据。
从实现角度看:是一种类模板,就体积而言 ,占总85%以上。

(2)算法

各种常用的算法,sort,seach,copy,erase....
从实现角度看:function template

(3)迭代器

容器和算法之间的胶合剂,又称为“泛型指针”,一共五种类型,以及其它衍生的变化。
从实现角度看:重载operator*,operator-> operator++,operator--等指针相关操作(class Template)
所有的STL都附带由自己的专属迭代器。

(4)仿函数

行为类似函数,内部重载了()的类或者类模板,一般的函数指针可以狭义的理解为仿函数

(5)配置器(配接器)

一种修饰容器或仿函数或者迭代器接口的东西,例如:stl提供的stack和queue,虽然看似是容器,其实只能算是一种容器配接器,因为他们底层完全借助于deque,所有操作都是由deque供应。
改变functor接口者,称为function adapter(bind,negate(否定),compose(组合))
改变container接口者,称为container adapter
改变iterator接口者,称为iterator adapter                              

(6)空间适配器

负责空间配置与管理(内存池技术)
从实现角度看:配置器实现了一个动态空间配置,空间管理,空间释放 (class Template)
SGI STL   第一级配置器 >128byte
SGI STL   第二级配置器 16个自由链表(8 16 24 32 40 48 56 64 72 80 88 96,104,112,120,128)

3.5 容器

3.5.1 容器分类

序列式容器(顺序容器):array(c++内建),vector,list,heap,prority-heap, slist  deque,stack,queue
					forward_list(c++11)
关联式容器:RB-tree(非公开) set map multiset multimap  hashtable hash-set hash-map 

3.5.2 确定使用哪种容器

通常,使用vector是最好的选择,除非你有更好的理由选择其它容器
1.如果你的程序有很多小的元素,且空间的额外开销很重要,则不要使用list或forward_list。
2.如果程序要求随机访问元素,应该使用vector或者deque
3.如果想在容器的中间插入或者删除元素,则list或forward_list应该是首选
4.如果程序要在头尾位置插入或删除元素,但是不会在中间为hi插入或删除元素,则使用deque。
5.如果想要在程序中随机访问元素,又要在容器的中间插入元素,该怎么办?
   -->答案取决于list或者forward_list中访问元素与vector或deque中插入、删除元素的相对性能。
   一般来说,应用中占主导地位的操作决定了容器类型的选择。
6.如果你还是不确定应该使用哪种容器,那么可以只是用vector或者list公共的操作:使用公共的操作:迭代器
    不使用下标操作,避免随机访问,这样使用两者,都比较方便。

3.5.3 vector容器的内存如何增长

3.5.4 vector的数据结构

template<class T,class Alloc = alloc>
class Vector
{
    protected:
    	iterator start;   //表示目前使用空间的头
    	iterator finish;  //表示目前使用空间的尾
    	iterator end_of_storage //表示目前可用空间的尾
        ...
};

在这里插入图片描述

#include <iostream>
#include <vector>
#include <list>
using namespace std;

class Test
{
public:
	int m_a;
	int m_b;
public:
	Test()
	{
		cout << "Test" << endl;
	}
	Test(int a):m_a(a)
	{
		cout <<m_a << " Test有一个参数的构造函数" << endl;
	}
	Test(const Test &obj)
	{
		cout << "Test的拷贝构造函数" << endl;
		this->m_a = obj.m_a;
		this->m_b = obj.m_b;
	}
	~Test()
	{
		cout << "~Test" << endl;
	}
};

int main(void)
{
//初始化
	vector<int> v1;
	vector<string> vs;
	vector<vector<string>> vv; //  其元素的类型是string的vector vector的vector
	vector<Test> v3;
	vector<int> v4 = { 1,2,3,4,5,6 }; //列表初始化
	vector<int> v5(v4);
	vector<int> v6(v4.begin() + 1, v4.end());
	vector<int> v7(100);   //预先开辟100个int
	vector<string> v8 = { 10,"hello" }; //10个hello
	vector<int> v9(100, 5);  //预先开辟100个int,全部初始化为5
//插入数据
	v3.reserve(10);
	cout << v3.capacity() << endl;
	v3.push_back(Test(1));  //末尾添加
	v3.push_back(Test(2));
	v3.push_back(Test(3));
	v3.insert(v3.end(), Test(4));  //按位置插入
	
	//v3.emplace_back(1);  //c++11
	//v3.emplace_back(2);
	//v3.emplace_back(3);
	//cout << v3.capacity() << endl;
	//v3.emplace(v3.begin() + 1, Test(5));  //中间插入
	//v3.resize(10); //重新指定容器的长度为10,len > 原来的长度,将没有使用的空间进行初始化,len < 原来的长度,将多余的数据丢弃。
	//v3.resize(10, 9);  //容量扩充为10,多出的部分初始化为9
	//cout << v3.capacity() << endl;
//遍历
	cout << v4.size() << endl;  //元素的个数
	cout << v4.capacity() << endl;  //容器的容量
	for (int i = 0; i < v4.size(); i++)
	{
		cout << v4[i] << " ";
	}
	cout << endl;

	for (vector<int>::size_type i = 0; i != 24; i++)
	{
		v1.push_back(i);
	}
	cout << v1.size() << endl;  //元素的个数
	cout << v1.capacity() << endl;  //容器的容量
	v1.reserve(50);
	cout << v1.size() << endl;  //元素的个数
	cout << v1.capacity() << endl;  //容器的容量
	while (v1.size() != v1.capacity())
	{
		v1.push_back(0);
	}
	cout << v1.size() << endl;  //元素的个数
	cout << v1.capacity() << endl;  //容器的容量
	v1.push_back(56);
	cout << v1.size() << endl;  //元素的个数
	cout << v1.capacity() << endl;  //扩充为75,具体依赖于标准库的实现
//归还内存
	v1.shrink_to_fit();
	cout << v1.size() << endl;  //元素的个数
	cout << v1.capacity() << endl;  //扩充为75,具体依赖于标准库的实现
//迭代器遍历vector

	/*for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it<<" ";
	}
	cout << endl;*/

	for (auto it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
//删除
	v1.erase(v1.begin());  //删除一个元素
	for (auto it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	v1.erase(v1.end() - 5, v1.end());
	for (auto it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	//使用迭代器删除v1中所有的奇数?
	//for (auto it = v1.begin(); it != v1.end();)
	//{
	//	cout << "begin1:" << *v1.begin() << endl;
	//	if ((*it % 2) != 0)
	//	{
	//		v1.erase(it);
	//	}
	//	else
	//	{
	//		it++;
	//	}
	//	cout << "begin2:" << *v1.begin() << endl;
	//	cout << endl;

	//}
	//for (auto it = v1.begin(); it != v1.end(); it++)
	//{
	//	cout << *it << " ";
	//}
	//cout << endl;
//assign 赋值(仅顺序容器)

	list<string> name;
	vector<const char *> oldstyle;
	//name = oldstyle;(错误)
	name.assign(oldstyle.cbegin(),oldstyle.cend());  //可以将const char*转为string
	return 0;

}

3.5.5 list

list好处是每次插入或者删除一个元素,就配置或释放一段空间,因此,对于list空间的运用有绝对的精准,一点也不浪费,而且对于任何位置的插入和删除,list永远是常数时间。
    list采用的迭代器:双向迭代器(bidirectional iterator)
#include <iostream>
#include <list>
#include <string>
using namespace std;
template <typename T>
void func(list<T> &l)
{
	for (auto it = l.begin(); it != l.end(); it++)
	{
		cout << *it <<" ";
	}
	cout << endl;
}
class Test
{
public:
	int m_a;
	string m_name;
public:
	Test()
	{
		cout << "Test" << endl;
	}
	Test(int a,string n):m_a(a), m_name(n)
	{
		cout <<m_a << " Test有一个参数的构造函数" << endl;
	}
	Test(const Test &obj)
	{
		cout << "Test的拷贝构造函数" << endl;
		this->m_a = obj.m_a;
		this->m_name = obj.m_name;
	}
	~Test()
	{
		cout << "~Test" << endl;
	}
	bool operator==(const Test &t)
	{
		return this->m_a == t.m_a && this->m_name == t.m_name;
	}
};
ostream& operator<<(ostream &out, Test &t)
{
	out << t.m_a <<" "<<t.m_name<<endl;
	return out;
}
int main(void)
{
	list<int> ll(100, 1);
	cout << ll.size() << endl;
	func(ll);
	list<int> l2(ll.begin(), ll.end());
	list<int> l3(l2);
	Test t1(1,"aaa");
	Test t2(2, "bbb");
	Test t3(3, "ccc");
	Test t4(4, "ddd");
	Test t5(5, "eee");
	Test t6(6,"fff");
	list<Test> l;
	l.push_back(t1);
	l.push_back(t2);
	l.push_back(t3);
	l.push_back(t4); //尾插法 

	l.push_front(t5); //头插法
	l.push_front(t6);
	l.push_front(Test(7, "ggg"));
	
	l.emplace_front(8, "ppp");
	l.emplace_back(9, "www");
	l.emplace(l.begin(), 10, "ooo");  //在开头插入
	func(l);//因为是要输出类,所以重载<<

//删除
	l.pop_back(); //删除最后一个结点
	l.pop_front();//删除第一个结点
	func(l); 
	cout << "********************" << endl;
//输出第一个元素
	cout << l.front() << endl;
//输出最后一个元素
	cout << l.back() << endl;
//链表扩充
	l.resize(15, t4);
	func(l);
//对象数组
	cout << "**********" << endl;
	Test t[5] = { Test{1,"a"}, Test{2,"b"}, Test{3,"c"}, Test{4,"d"}, Test{5,"e"} };
	l.insert(l.begin(), t[0]);  //在头部插入一个元素
	l.insert(l.end(), t, t + 5);  //在链表尾部插入一个区间
	func(l);

//删除一个区间
	//l.erase(++l.begin(),--l.end());
	//func(l);
//删除某一个位置
	//l.erase(l.begin());
	//func(l);
//删除一个具体的元素
	//l.remove(t4);
	//func(l);
//c++20:unique(重载==)
	l.unique(); //相邻重复的全部删除
	func(l);
/*重载==*/
	l.sort();
	
	return 0;

}

猜你喜欢

转载自blog.csdn.net/m0_52592798/article/details/125941772
今日推荐