嵌入式C++开发详解(一)

时间:2022-03-29 21:06:58

嵌入式C++开发详解(一)

一、C++概述

1.嵌入式开发中为什么选择C++语言?

(1)面向过程编程的特点

  C语言特点:C语言是在实践的过程中逐步完善的

             ·没有深思熟虑的设计过程

               ·使用时存在很多“灰色地带”

                ……

             ·残留量过多低级语言的特征

               ·直接利用指针进行内存操作

                ……

  面向过程的编程特点:

             面向过程程序设计:数据结构+算法

  ·主要解决科学计算问题,用户需求简单固定

  ·特点:分析解决问题所需要的步骤

          利用函数实现各个步骤

          依次调用函数解决问题

  ·问题:软件可重用性差

          软件可维护性差

          构建的软件无法满足用户需求

 

(2)面向对象编程的特点

  面向对象的编程特点:

            面向对象程序设计:由现实世界建立软件模型

   ·将现实世界中的事物直接映射到程序中,可直接满足用户需求

   ·特点:直接分析用户需求中涉及的各个实体

           在代码中描述现实世界中的实体

           在代码中关联各个实体协同工作解决问题

   ·优势:构建的软件能够适应用户需求的不断变化

           直接利用面向过程方法的优势而避开其劣势

  C++语言特点:高效的面向对象语言,并且能够兼容已经存在的代码

 嵌入式C++开发详解(一)

2.C++为什么难学?

C++支持的编程格式:

·过程式

·数据抽象

·基于对象

·面向对象式

·函数式

·泛型形式

·模板元形式

值语义和对象语义:

值语义可以拷贝与赋值,对象语义不可进行拷贝与赋值

3.C++相关基础知识点

(1)C++之父是谁?

    本贾尼·斯特劳斯特卢普

1982年,美国AT&T公司贝尔实验室的Bjarne Stroustrup博士在c语言的基础上引入并扩充 了面向对象的概念,发明了—种新的程序语言。为了表达该语言与c语言的渊源关系,它被命名为C++。而Bjarne Stroustrup(本贾尼·斯特劳斯特卢普)博士被尊称为C++语言之父。

(2)C++语言的标准

  C++ 98 标准

  C++标准第一版,1998年发布。正式名称为ISO/IEC 14882:1998[17]  。

  C++ 03 标准

  C++标准第二版,2003年发布。正式名称为ISO/IEC 14882:2003[18]  。

  C++ 11 标准

  C++标准第三版,2011年8月12日发布。正式名称为ISO/IEC 14882:2011[19]  。

  C++11对容器类的方法做了三项主要修改。

  首先,新增的右值引用使得能够给容器提供移动语义。其次,由于新增了模板类initilizer_list,  

  因此新增了将initilizer_list作为参数的构造函数和赋值运算符。第三,新增的可变参数模板(variadic template)和函数参数包(parameter pack)使得可以提供就地创建(emplacement)方法。

  C++ 14 标准

  C++标准第四版,2014年8月18日发布。正式名称为ISO/IEC 14882:2014[21]  。

  C++14是C++11的增量更新,主要是支持普通函数的返回类型推演,泛型 lambda,扩

  展的 lambda 捕获,对 constexpr 函数限制的修订,constexpr变量模板化等[22]  。

(3)C++11值得学习的新特性

·智能指针如shared_ptr、weak_ptr等

·rvalue reference

·function/bind

·lambda expression and closure

二、从C到C++的升级

1.声明定义

 C++:C++中更强调语言的实用性,所有变量都可以在需要使用时再定义

     如:for(int i = 0; i < 2; i++)

 C语言:变量必须在作用域开始时定义

2.register关键字的升级

   经常被访问的变量我们就可以用register修饰为寄存器变量,请求编译器尽可能的将变量存在CPU的内部寄存器中,节省了CPU从内存中抓取数据的时间,从而提高了运行效率。

 C语言:register只能修饰局部变量,不能修饰全局变量和函数;

         register修饰的变量不能通过取地址来获取寄存器变量;

         register修饰的变量一定是CPU能接受的数据类型。

 C++:在C++中依然支持register关键字

      C++编译器有自己的优化方式,不使用register中也可能做优化

      C++中可以取register变量的地址(C++编译器发现程序中需要取register

      变量的地址时,register对变量声明无效)

3.const关键字

C++:C++编译器对const常量的处理

     ·当碰见常量声明时在符号表中放入常量

     ·编译过程中若发现使用常量则以符号表中的值替换

     ·编译过程中若发现对const使用了extern或者&操作符,则给对应的常量

       分配存储空间

  注意:C++编译器虽然可能为const常量分配空间,但不会使用其存储空间中

        的值。

C语言:C语言中const修饰变量,空间里的值可变,但是不能通过变量名来修改这个空间的对应值。

4.内存分配与释放

(1)C++中动态内存分配

·C++中通过new关键字进行动态内存申请

·C++的动态内存申请是基于类型进行的

·delete关键字用于内存释放

变量申清:

Type *pointer = new Type;

……

delete pointer;

数组申清:

Type *pointer = new Type[];

……

delete[] pointer;

(2)new与malloc区别

·new关键字是C++的一部分,malloc是由C库提供的函数

·new以具体类型为单位进行内存分配,malloc只能以字节为单位进行内存

  分配

·new在申请单个类型变量时可进行初始化,malloc不具备内存初始化的特

  性

5.引用VS指针

(1)引用

     ·引用是给一个变量起别名

     ·定义一个引用的一般格式:

          ·类型 &引用名 = 变量名

          ·例如:int a = 1;

                  int &b = a; //b是a的别名,因此a和b是同一个单元

      注意:定义引用时一定要初始化,指明该引用变量是谁的别名

     ·在实际应用中,引用一般用作参数传递与返回值

(2)const引用是指向const对象的引用

(3)函数传参:按引用传递

引用传递方式是在函数定义时在形参前面加上运算符“&”

例如:void swap(int &a,int &b);

·按值传递方式容易理解,但形参值得改变不能对实参产生影响

·地址传递方式通过形参的改变使响应的实参改变,但程序容易产生错误且难以阅读

·引用作为参数对形参的任何操作都能改变相应的实参的数据,又使函数调用显得方便、自然。

(4)函数返回值:引用作为函数返回值

·引用的另一个作用是用于返回引用的函数

·函数返回引用的一个主要目的是可以将函数放在赋值运算符的左边

·注意:不能返回对局部变量的引用

(5)引用与指针的区别

·引用访问一个变量是直接访问,而指针是间接访问

·引用是一个变量的别名,本身不单独分配自己的内存空间,而指针有自己的内存空间

·引用一经初始化不能再引用其它变量,而指针可以

·尽可能使用引用,不得已时使用指针

6.函数升级

(1)内联函数(inline):以空间换时间(频繁调用且简短)

      内联函数的使用:

          inline int max(int a,int b)

          {

               return a>b?a:b;

          }

          #define MAX(a,b) (a)>(b)?(a):(b)

      内联函数与带参数宏的区别:

     ·内联函数调用时,要求实参和形参的类型一致,另外内联函数会先对实

       参表达式进行求值,然后传递给形参;而宏调用只用实参简单地替换形

       参。

     ·内联函数是在编译的时候、在调用的时候将代码展开的,而宏则是在预

       处理时进行替换的

     ·在C++中建议有用inline函数来替换带参数的宏

(2)函数重载(overload)

    相同的作用域,如果两个函数名称相同,而参数不同,我们把它们称为重载overload

函数重载的条件:(函数的返回值不能作为条件)

  ·函数重载不同形式:

        ·形参数量不同

        ·形参类型不同

        ·形参顺序不同

        ·形参数量和形参类型都不同

  ·调用重载函数时,编译器通过检查参数的个数、类型和顺序来确定相应

    的被调用函数

     name managling与extern “C”:

     ·name managling 这里把它翻译为名字改编

     ·C++为了支持重载,需要进行name managling

     ·externC实现了C与C++混合编程

           #ifdef__cpluscplus

           externC

           {

                #endif

                 ……

                #ifdef__cpluscplus

           }

           #endif

(3)带默认参数的函数:

     ·函数没有声明时,在函数定义中指定形参的默认值

     ·函数既有定义又有声明时,声明时指定后,定义后就不能再指定默认值

     ·默认值的定义必须遵守从右到左的顺序,如果某个形参没有默认值,则

       它左边的参数就不能有默认值

       void funcl(int a,double b = 4.5,int c = 3)   //合法

       void funcl(int a = 1,double b ,int c = 3)   //不合法

     ·函数调用时,实参与形参按从左到右的顺序进行匹配

 

 7.命名空间-namespace

(1)命名空间

    ·在C语言中只有一个全局作用域

     ·C语言中所有的全局标识符共享一个作用域

         ·标识符之间可能有冲突

·C++中提出了命名空间的概念

     ·命名空间将全局作用域分成不同的部分

     ·不同命名空间中的标识符可以同名而不会发生冲突

     ·命名空间可以相互嵌套

     ·全局作用域也叫默认命名空间

 

 

(2)如何定义命名空间

namespace First

{

     int i = 0;

}

namespace Second

{

int i = 1;

}

 

using namespace First;

 

int main()

{

cout<<i<<endl;

cout<<Second::i<<endl;

return 0;

}

(3)如何使用命名空间

·使用整个命名空间:using namespace name;

·使用命名空间中的变量:using name::variable;

·使用默认命名空间中的变量:  ::variable

 

默认情况下可以直接使用

默认命名空间中的所有标识符

 

8.新的类型转换运算符

(1)static_cast<T>(expr)

·用于基本类型间的转换,但不能用于基本类型指针间的转换

·用于有继承关系类对象之间的转换和类指针之间的转换

 

int main()

{

int i = 0;

char c = c;

int *pi = &i;

char *pc = &c;

 

c = static_cast<char>(i);//success

pc = static_cast<char *>(pi);//error

    return 0;

}

static_cast是在编译期进行转换的,无法在运行时检测类型,所以类类型之间的转换可能存在风险

(2)const_cast<T>(expr)

const_cast强制类型转换——用于去除变量的const属性

int main()

{

    const int &j = 1;

    int & k = const_cast<int&>(j);

    const int x = 2;

        int & y = const_cast<int&>(x);

 

        k = 5;

 

        cout << j << endl;

        cout << k << endl;

 

        y = 3;

 

        cout <<x<<endl;

        cout<<y<<endl;

        cout <<&x<<endl;

        cout<<&y<<endl;

 

        return 0;

}

(3)reinterpret_cast<T>(expr)  

  reinterpret_cast强制类型转换

·用于指针类型间的强制转换

·用于整数和指针类型间的强制转换

typedef void(pf)(int)

int main()

{

int i = 0;

char c = c;

int * pi = reinterpret_cast<int *>(&c);

char * pc = reinterpret_cast<char *>(&i);

PF * pf = reinterpret_cast<PF*>(0x12345678);

 

c = reinterpret_cast<char>(i);

 

return 0;

}

reinterpret_cast直接从二进制位进行赋值,是一种极其不安全的转换。

(4)dynamic_cast<T>(expr)

   dynamic_cast强制类型转换

 ·主要用于类层次间的转换,还可以用于类之间的交叉转移

·dynamic_cast还具有类型检查的功能,比static_cast更安全