嵌入式linux C++语言(二)——C++对C语言基础语法的扩展

嵌入式linux C++语言(二)——C++对C语言基础语法的扩展

C++是基于C语言扩展发展而来的面向对象的程序设计语言,本文将主要讨论C++语言基于C语言扩展的方面。

一、类型增强

1、类型检查更严格

在C语言中:

const int a = 100;

int *p = &a;

在C++语言中:

const int a = 100;//必须在定义的时候初始化

const int *p = &a;

在C++语言中不能隐式转换数据类型。

error: invalid conversion from ‘const int*‘ to ‘int*‘ [-fpermissive]

2、bool类型

C语言中表示真用非0,假用0

C++定义了自己的bool类型,真为true,假为false

sizeof(bool) = 1

3、枚举

C语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而c++中枚举变量,只能用被枚举出来的元素初始化。

在C语言中,枚举的使用

#include <stdio.h>

enum weekday

{

monday,tuesday,wednesday,thursday,friday,saturday,sunday

};

int main(int argc, char **argv)

{

enum weekday day = monday;

enum weekday a = sunday;

enum weekday b = 100;

//weekday c = sunday;错误用法,需要使用enum声明

printf("%d %d %d\n", day, a, b);

return 0;

}

在C++语言中枚举的使用

enum weekday

{

monday,tuesday,wednesday,thursday,friday,saturday,sunday

};

int main()

{

weekday day = sunday;

enum weekday a = monday;

cout<<day<<" "<<a<<endl;

return 0;

}

4、表达式做左值

C语言中表达式不可以做左值,C++中某些表达式可以做左值。

(a = b) = 6;

二、输入与输出

1、cin&cout

cin和cout是C++的标准输入流和输出流,在头文件 iostream 中定义。

流名     含义         隐含设备     流名     含义             隐含设备
cin                标准输入     键盘                     cerr         标准错误输出     屏幕
cout             标准输出     屏幕                     clog         cerr 的缓冲输出    屏幕

int main()
{
    char name[30];
    int age;
    cout<<"pls input name and age:"<<endl;
    cin>>name;
    cin>>age;
    cout<<"your name is: "<<name<<endl;
    cout<<"your age is: "<<age<<endl;
    return 0;
}

2、格式化

A、按进制输出数据类型

cout<<dec<<i<<endl;
cout<<hex<<i<<endl;
cout<<oct<<i<<endl;

B、设置域宽,设置左右对齐及填充字符

int main()
{
    cout<<setw(10)<<1234<<endl;
    cout<<setw(10)<<setfill(‘0‘)<<1234<<endl;
    cout<<setw(10)<<setfill(‘0‘)<<setiosflags(ios::left)<<1234<<endl;
    cout<<setw(10)<<setfill(‘-‘)<<setiosflags(ios::right)<<1234<<endl;
    return 0;
}

C、实型数据的设置

cout<<setw(5)<<‘a‘<<endl<<setw(5)<<100<<endl
<<setprecision(2)<<setiosflags(ios::fixed)<<120.00<<endl;

三、函数重载

C语言中不允许重名函数的存在,C++语言为了简化编程允许重名函数的存在,重名函数称为函数重载。

int abs(int a)
{
    return a>0? a:-a;
}
double abs(double a)
{
    return a>0? a:-a;
}

1、重载规则

A、函数名相同。
B、参数个数不同,参数的类型不同,参数顺序不同,均可构成重载。
C、返回值类型不同则不可以构成重载。

2、函数重载的匹配规则

A、严格匹配,找到则调用。
B、通过隐式转换寻求一个匹配,找到则调用。

C++允许int到long和double的隐式类型转换,因此遇到这种情型,则会引起二义性,解决方法是在调用时强转类型。

3、函数重载的底层实现

C++利用 name mangling(倾轧)技术,来改名函数名,区分参数不同的同名函数。
    实现原理:用 vci f l d表示void char int float long double及其引用。

void func(char a); // func_c(char a)
void func(char a, int b, double c);//func_cid(char a, int b, double c);

name mangling发生在两个阶段,.cpp编译阶段,和.h的声明阶段。只有两个阶段同时进行,才能匹配调用。

C++完全兼容C语言,因此必须完全兼容C的类库。由于.c文件的类库文件中函数名并没有发生name manling行为,而在包含.c文件所对应的.h文件时,.h 文件要发生name manling行为,因而会在编译链接时候发生错误。
        C++为了避免上述错误的发生,重载了关键字extern。只需要要避免name manling的函数前,加extern "C"如有多个,则extern "C"{}。

C语言标准库中实际上对C++语言程序引用时做了特殊处理,在C++语言编译器编译时使用extern "C"将C语言的标准库函数排除了命名倾轧。

#ifdef __cplusplus

extern “C”{

#endif

标准库函数

#ifdef __cplusplus

}

#endif

四、操作符重载

C++提供了运算符重载机制。可以为自定义数据类型重载运算符。实现构造数据类型也可以像基本数据类型一样的运算特性。

struct COMP
{
    float real;
    float image;
};
COMP operator+(COMP one, COMP another)
{
    one.real += another.real;
    one.image += another.image;
    return one;
}
int main()
{
    COMP c1 = {1,2};
    COMP c2 = {3,4};
    COMP sum = operator+(c1,c2); //c1+c2;
    cout<<sum.real<<" "<<sum.image<<endl;
    return 0;
}

实例代码重载了一个全局的操作符+号用于实现将两个自定义结构体类型相加。本质是函数的调用。

五、默认参数

函数在调用时,形参从实参那里取得值。对于多次调用用一函数同一实参时,C++给出了更简单的处理办法。给形参以默认值,这样就不用从实参那里取值了。

1、默认参数的规则

A、默认参数的顺序,是从右向左,不能跳跃。
B、定义在前,调用在后(此时定义和声明为一体),默认认参数在定义处;声明在 前,调用在后,默认参数在声明处。
C、一个函数,不能既作重载,又作默认参数的函数。当你少写一个参数时,系统无法确认是重载还是默认参数。

2、使用示例

A、单个参数

void weatherForcast(char * w="sunny")

{
    time_t t = time(0);
    char tmp[64];
    strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A ",localtime(&t) );
    cout<<tmp<< "today is weahter "<<w<<endl;
}
int main()
{
    //sunny windy cloudy foggy rainy
    weatherForcast();
    weatherForcast("rainny");
    weatherForcast();
    return 0;
}

B、多个参数

float volume(float length, float weight = 4,float high = 5)
{
    return length*weight*high;
}
int main()
{
    float v = volume(10);
    float v1 = volume(10,20);
    float v2 = volume(10,20,30);
    cout<<v<<endl;
    cout<<v1<<endl;
    cout<<v2<<endl;
    return 0;
}

六、引用

变量名,本身是一段内存的引用,即别名(alias)。引用,是为己有变量起一个别名。

int a;
int &b = a;

1、引用的规则

A、引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故 而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
B、声明的时候必须初始化,一经声明,不可变更。
C、可对引用再次引用。多次引用的结果,是某一变量具有多个别名。
D、&符号前有数据类型时,是引用。其它皆为取地址。

int a,b;
int &r = a;
int &r = b; //错误,不可更改原有的引用关系

r = b;//正确,赋值
float &rr = b; //错误,引用类型不匹配
cout<<&a<<&r<<endl; //变量与引用具有相同的地址。
int &ra = r; //可对引用更次引用,表示 a 变量有两个别名,分别是 r 和 ra

2、引用的应用

void swap(int &a, int &b)

{
    int tmp;
    tmp = a;
    a = b;
    b = tmp;
}

void swap(char * &a, char * &b)

{
        char *t = a;

a = b;
    b = t;
}

3、引用的提高

A、可以定义指针的引用,但不能定义引用的引用。

int a;
int* p = &a;
int*& rp = p; // ok
int& r = a;
int&& rr = r; // error

B、可以定义指针的指针(二级指针),但不能定义引用的指针。

int a;
int* p = &a;
int** pp = &p; // ok
int& r = a;
int&* pr = &r; // error

C、可以定义指针数组,但不能定义引用数组,可以定义数组引用。

int a, b, c;
int* parr[] = {&a, &b, &c}; // ok
int& rarr[] = {a, b, c}; // error
int arr[] = {1, 2, 3};
int (&rarr)[3] = arr; // ok 的

E、常引用

const对象的引用必须是const的,将普通引用绑定到const对象是不合法的。const引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化,是const引用与普通引用最大的区别。 const int &a=2;是合法的。 double x=3.14; constint &b=a;也是合法的。

非const引用只能绑定到与该引用同类型的对象。

4、引用的本质

引用的本质是一个常量指针。

七、new/delete

C语言中提供了malloc和free两个系统函数,完成对堆内存的申请和释放。而C++则提供了两关键字new和delete 。

1、new

A、开辟单变量地址空间

int *p = new int; //开辟大小为 sizeof(int)空间
int *a = new int(5); //开辟大小为 sizeof(int)空间,并初始化为 5

B、开辟数组空间

一维: int *a = new int[100];开辟一个大小为 100 的整型数组空间

二维: int (*a)[6] = new int[5][6]
 三维: int (*a)[5][6] = new int[3][5][6]

2、delete

A、释放单变量空间

int *a = new int;

delete a; //释放单个 int 的空间

B、释放数组空间

int *a = new int[5];

delete []a; //释放 int 数组空间

3、new/delete应用

int *p = new int(5);
cout<<*p<<endl;
delete p;

char *pp = new char[10];
strcpy(pp,"china");
cout<<pp<<endl;
delete []pp;

string *ps = new string("china");
cout<<*ps<<endl; //cout<<ps<<endl;
delete ps;

char **pa= new char*[5];
memset(pa,0,sizeof(char*[5]));
pa[0] = "china";
pa[1] = "america";
char **pt = pa;
while(*pt)
{
    cout<<*pt++<<endl;
}
delete []pt;

int (*qq)[3][4] = new int[2][3][4];
delete []qq;

4、返回值

//C++ 内存申请失败会抛出异常
try{
    int *p = new int[10];
}

catch(const std::bad_alloc e)

{
    return -1;
}

//C++ 内存申请失败不抛出异常版本
int *q = new (std::nothrow)int[10];
if(q == NULL)
    return -1;

5、注意事项

A、new/delete 是关键字,效率高于 malloc 和 free.
B、配对使用,避免内存泄漏和多重释放。
C、避免交叉使用。比如 malloc 申请的空间去 delete,new 出的空间被 free;

D、重点用在类对像的申请与释放。申请的时候会调用构造器完成初始化,
释放的时候,会调用析构器完成内存的清理。

八、内联函数

C语言中有宏函数的概念。宏函数的特点是内嵌到调用代码中去,避免了函数调用的开销。但宏函数的处理发生在预处理阶段,缺失了语法检测和有可能带来的语意差错。C++提供了inline关键字,实现了真正的内嵌,优点是高度抽象,避免重复开发,具备类型检查,缺点是压栈与出栈,带来开销。

inline int sqr(int x)
{
return x*x;
}

优点:避免调用时的额外开销(入栈与出栈操作)
    缺点:内联函数的函数体在代码段中会出现多个“副本”,因此会增加代码段的空间。
    本质:以牺牲代码段空间为代价,提高程序的运行时间的效率。
    适用场景:函数体很“小”,且被“频繁”调用。

inline实际上是是对编译器的建议,如果编译器认为inline声明的函数可以内联,则编译器会将函数内联,如果编译器认为inline声明的函数的函数体太长,则不会内联,按普通函数处理。

九、类型强转

C语言中的类型转换是强制转换,

1、静态类型转换

静态类型转换是在编译期内即可决定其类型的转换。

语法格式:
static_cast<目标类型> (标识符)

int a = 10;
int b = 3;
cout<<static_cast<float>(a)/b<<endl;

2、动态类型转换

语法格式:
dynamic_cast<目标类型> (标识符)
用于多态中的父子类之间的强制转化

2、常量类型转换

语法格式:
const_cast<目标类型> (标识符) //目标类类型只能是指针或引用。

const_cast将转换掉表达式的const属性

4、重解释类型转换

语法格式:
reinterpret_cast<目标类型> (标识符)

为数据的二进制形式重新解释,但是不改变其值。

十、命名空间

命名空间为了大型项目开发,而引入的一种避免命名冲突的一种机制。比如说,在一个大型项目中,要用到多家软件开发商提供的类库。在事先没有约定的情况下,两套类库可能在存在同名的函数或是全局变量而产生冲突。项目越大,用到的类库越多,开发人员越多,这种冲突就会越明显。

1、默认NameSpaceglobal &&function

global scope是一个程序中最大的scope,是引起命名冲突的根源。C语言没有从语言层面提供这种机制来解决。global scope是无名的命名空间。

2、语法规则

NameSpace是对全局区域的再次划分。

A、声明及namespace 中可以包含的内容

namespace NAMESPACE
{
   全局变量int a;
    数据类型struct Stu{};
    函数 void func();
}

B、使用方法

直接指定 命名空间: NameSpace::a = 5;
    使用 using+命名空间+空间元素:using NameSpace::a; a = 2000;
    使用 using +namespace+命名空间;

C、无可避免的冲突

#include <iostream>
using namespace std;
namespace MySpace
{
    int x = 1;
    int y = 2;
}
namespace Other {
    int x = 3;
    int y = 4;
}
int main()
{
    {
        using namespace MySpace;
        cout<<x<<y<<endl;
    }

{
    using namespace Other;
    cout<<x<<y<<endl;
    }

{
        MySpace::x = 100;
        Other::y = 200;
        cout<<MySpace::x<<Other::y<<endl;
    }
    return 0;
}

可以使用块语句将命名空间限定在块语句内部。

D、支持嵌套

namespace MySpace

{
    int x = 1;
    int y = 2;
    namespace Other {
        int m = 3;
        int n = 4;
    }
}

E、协作开发

在实际项目开发中,可以将一个类或者具有相同属性的多个类声明在一个命名空间内,在使用时只需要声明命名空间即可。

#ifndef A_H
#define A_H
namespace XX {
class A
{
public:
    A();
    ~A();
};
}
#endif // A_H

#include "a.h"
using namespace XXX
{
    A::A()
    {}
    A::~A()
    {}

}

十一、系统string类

除了使用字符数组来处理字符串以外,C++引入了字符串类型。可以定义字符串变量。

1、定义和初始化

string str;

str = "china";
string str2 = " is great ";
string str3 = str2;

2、类型大小

cout<<sizeof(string)<<" "<<str.max_size()<<str.size()<<endl;

3、运算

A、赋值

string str3 = str2;

B、加法

string combine = str + str2;

C、关系

string s1 = "abcdeg";

string s2 = "12345";

if(s1>s2)

cout<<"s1>s2"<<endl;

else

cout<<"s1<s2"<<endl;

4、string类型数组

string数组是高效的,如果用二维数组来存入字符串数组的话,则容易浪费空间,此时列数是由最长的字符串决定。如果用二级指针申请堆空间,依据大小申请相应的空间,虽然解决了内存浪费的问题,但是操作麻烦。用 string 数组存储,字符串数组的话,效率即高又灵活。

string sArray[10] = {
"0",
"1",
"22",
"333",
"4444",
"55555",
"666666",
"7777777",
"88888888",
"999999999",
};
for(int i=0; i<10; i++)
{
cout<<sArray[i]<<endl;
}

5、string类的成员函数

int capacity()const;  //返回当前容量(即string中不必增加内存即可存放的元素个数)
int max_size()const;    //返回string对象中可存放的最大字符串的长度
int size()const;        //返回当前字符串的大小
int length()const;       //返回当前字符串的长度
bool empty()const;        //当前字符串是否为空
void resize(int len,char c);//把字符串当前大小置为len,并用字符c填充不足的部分

十二、C语言开发人员的建议

1、在 C++中几乎不需要用宏,用const或enum定义明显的常量,用inline避免函数调用的额外开销,用模板去刻画一族函数或类型,用namespace去避免命名冲突。
2、不要在你需要变量之前去声明,以保证你能立即对它进行初始化。
3、不要用malloc,new运算会做的更好
4、避免使用 void*、指针算术、联合和强制,大多数情况下,强制都是设计错误的指示器。
5、尽量少用数组和C风格的字符串,标准库中的string和vector可以简化程序
6、更加重要的是,试着将程序考虑为一组由类和对象表示的相互作用的概念,而不是一堆数据结构和一些可以拨弄的二进制。

时间: 2024-10-13 10:14:29

嵌入式linux C++语言(二)——C++对C语言基础语法的扩展的相关文章

嵌入式Linux ARM汇编(七)——C语言与ARM汇编混合编程

嵌入式Linux ARM汇编(七)--C语言与ARM汇编混合编程 在嵌入式系统开发中,目前使用的主要编程语言是C和汇编.在大规模的嵌入式软件中,例如含有OS,大部分的代码都是用C编写的,主要是因为C语言的结构比较好,便于人的理解,而且有大量的支持库.但是很多地方还是要用到汇编语言,例如开机时硬件系统的初始化,包括CPU状态的设定,中断的使能,主频的设定,以及RAM的控制参数及初始化,一些中断处理方面也可能涉及汇编.另外一个使用汇编的地方就是一些对性能非常敏感的代码块,这是不能依靠C编译器的生成代

嵌入式 Linux进程间通信(二)——exec族函数

嵌入式 Linux进程间通信(二)--exec族函数 exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件.这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件. exec族函数包含如下函数: #include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ...); int exec

C++语言学习(二)——C++对C语言基础语法的扩展

C++语言学习(二)--C++对C语言基础语法的扩展 C++是基于C语言扩展发展而来的面向对象的程序设计语言,本文将主要讨论C++语言基于C语言扩展的方面. 一.实用性增强 C语言中变量的定义必须在作用域开始的位置进行定义. #include <stdio.h> int main(int argc, char *argv[]) { int i;//定义变量 int j; //使用变量 for(i = 0; i < 10; i++) { for(j = 0; j < 10; j++)

Java基础知识二次学习-- 第二章 基础语法与递归补充

第二章 基础语法与递归补充   时间:2017年4月24日10:39:18 章节:02章_01节,02章_02节 视频长度:49:21 + 15:45 内容:标识符,关键字与数据类型 心得:由字母,下划线,$,数字组成,应该由字母,下划线$开头,同时应该避开java保留字符 变量是内存中的一小块区域,使用变量名来访问这块区域 执行过程中的内存管理(疑问:这里的内存和Jvm的一样吗?) code segment 存放代码 data segment 静态变量 字符串常量 stack 栈 局部变量 h

嵌入式linux C++语言(一)——C++简介

嵌入式linux C++语言(一)--C++简介 一.C++简介 C语言作是结构化和模块化的语言,适合处理较小规模的程序.对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言并不合适.为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming)思想,支持面向对象的程序设计语言应运而生.Smalltalk 就是当时问世的一种面向对象的语言.在实践工作中,由于C语言的广泛使用,在C语言的基础上根据面向对象的思想发展了C语言,形成了C

作为一个新人,怎样学习嵌入式Linux?

作为一个新人,怎样学习嵌入式Linux?被问过太多次,特写这篇文章来回答一下. 在学习嵌入式Linux之前,肯定要有C语言基础.汇编基础有没有无所谓(就那么几条汇编指令,用到了一看就会).C语言要学到什么程度呢?越熟当然越好,不熟的话也要具备基本技能.比如写一个数组排序.输入数字求和什么的.学C语言唯一的方法是多写程序多练习,编译出错没关系,自己去解决:执行出错没关系,自己去分析.以前我是用VC来练习C语言的,经常去尝试着写一些C语言竞赛的题目.它们是纯C.纯数学.纯逻辑的题目,不涉及界面这些东

作为一个新人,怎样学习嵌入式Linux,(韦东山)

很早以前在网上看到的韦东山老师写的文章,复制到自己的博客,方便自己以后看. 在学习嵌入式Linux之前,肯定要有C语言基础.汇编基础有没有无所谓(就那么几条汇编指令,用到了一看就会). C语言要学到什么程度呢?越熟当然越好,不熟的话也要具备基本技能.比如写一个数组排序.输入数字求和什么的. 学C语言唯一的方法是多写程序多练习,编译出错没关系,自己去解决:执行出错没关系,自己去分析.以前我是用 VC来练习C语言的,经常去尝试着写一些C语言竞赛的题目.它们是纯C.纯数学.纯逻辑的题目,不涉及界面这些

作为一个新人,如何学习嵌入式Linux?

作为一个新人,如何学习嵌入式Linux?被问过太多次,特写这篇文章来回答一下. 在学习嵌入式Linux之前.肯定要有C语言基础.汇编基础有没有无所谓(就那么几条汇编指令,用到了一看就会).C语言要学到什么程度呢?越熟当然越好,不熟的话也要具备基本技能.比方写一个数组排序.输入数字求和什么的.学C语言唯一的方法是多敲代码多练习,编译出错没关系,自己去解决:运行出错没关系.自己去分析.曾经我是用VC来练习C语言的.常常去尝试着写一些C语言竞赛的题目. 它们是纯C.纯数学.纯逻辑的题目.不涉及界面这些

嵌入式linux面试题解析(二)——C语言部分二

嵌入式linux面试题解析(二)--C语言部分二 1..h头文件中的ifndef/define/endif 的作用?    答:防止该头文件被重复引用. 2.#include 与 #include "file.h"的区别?    答:前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h. 3.描述实时系统的基本特性    答 :在特定时间内完成特定的任务,实时性与可靠性. 4.全局变量和局部变量在内存中是否有区别?如果有,是