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

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

sizeofbool= 1

3、枚举

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

C语言中,枚举的使用

#include

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<

    return 0;

}

4、表达式做左值

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

(a = b) = 6;

二、输入与输出

1、cin&cout

cincoutC++的标准输入流和输出流在头文件 iostream 中定义。

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

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

2、格式化

A、按进制输出数据类型

cout<
cout<
cout<

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

int main()
{
    cout<
    cout<
    cout<
    cout<
    return 0;
}

C实型数据的设置

cout<
<

三、函数重载

    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++允许intlongdouble隐式类型转换,因此遇到这种情型,则会引起二义性,解决方法是调用时强转类型。

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<
    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<
}
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<
    cout<
    cout<
    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<变量与引用具有相同的地址。
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语言中提供了mallocfree两个系统函数,完成对堆内存的申请和释放。而C++则提供了两关键字newdelete

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 数组空间

3new/delete应用

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


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

 

string *ps = new string("china");
cout<<*ps<
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++<
}
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、注意事项

Anew/delete 是关键字,效率高于 malloc free.
B配对使用,避免内存泄漏和多重释放。
C避免交叉使用。比如 malloc 申请的空间去 deletenew 出的空间被 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<(a)/b<

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

   {
    using namespace Other;
    cout<
    }

   {
        MySpace::x = 100;
        Other::y = 200;
        cout<
    }
    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<<<

3、运算

A、赋值

string str3 = str2;

B、加法

string combine = str + str2;

C、关系

string s1 = "abcdeg";

string s2 = "12345";

if(s1>s2)

cout<<"s1>s2"<

else

cout<<"s1

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

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


本文出自 “生命不息,奋斗不止” 博客,转载请与作者联系!

你可能感兴趣的:(C++语言)