嵌入式c++(十)

简介: 嵌入式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 使用



         


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;
}


相关文章
|
7月前
|
开发框架 Linux C语言
C、C++、boost、Qt在嵌入式系统开发中的使用
C、C++、boost、Qt在嵌入式系统开发中的使用
229 1
|
7月前
|
算法 Linux 程序员
嵌入式工程师以及C++程序员到公司就业需要掌握那些技术?
嵌入式工程师以及C++程序员到公司就业需要掌握那些技术?
|
7月前
|
数据处理 C++ UED
如何作为一个嵌入式软件工程师博主获得铁粉:C/C++ 技术分享之道
如何作为一个嵌入式软件工程师博主获得铁粉:C/C++ 技术分享之道
125 0
|
7月前
|
C语言 数据安全/隐私保护 C++
嵌入式中如何把C++代码改写成C语言代码
嵌入式中如何把C++代码改写成C语言代码
82 0
|
7月前
|
存储 缓存 Java
嵌入式系统中C++内存管理基本方法
嵌入式系统中C++内存管理基本方法
138 0
|
7月前
|
存储 编译器 程序员
嵌入式系统中C++基础知识精髓
嵌入式系统中C++基础知识精髓
114 0
|
7月前
|
关系型数据库 数据库 C++
嵌入式数据库sqlite3【基础篇】基本命令操作,小白一看就懂(C/C++)
嵌入式数据库sqlite3【基础篇】基本命令操作,小白一看就懂(C/C++)
|
7月前
|
存储 编译器 C++
嵌入式中C++ 编程习惯与编程要点分析
嵌入式中C++ 编程习惯与编程要点分析
58 1
|
7月前
|
架构师 数据挖掘 程序员
嵌入式系统中C++ 类的设计和实现分析
嵌入式系统中C++ 类的设计和实现分析
75 1
|
7月前
|
算法 小程序 编译器
嵌入式中C++开发的基本操作方法
嵌入式中C++开发的基本操作方法
63 0