C++对C的升级

时间:2022-09-09 17:31:06

可能大家对C语言不会陌生,用C语言写起代码来也是得心应手。但是在做项目的时候,C语言的复用性、扩展性、维护性就比较差!如果我们在原来的基础上添加新的功能,可能就需要修改大量的代码,相信这种方式你是不会采取的!C语言是面向过程的语言,这里需要有一种面向对象的语言。于是C++语言就诞生了!

一、C++语言的特性

1、C++完全兼容C的特性。

2、C++在C语言的基础上进行了升级。

3、C++复用性、扩展性、维护性更好。


二、C++对C的升级

1、声明定义

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

#include <stdio.h>

int main(int argc, char *argv[])
{
int c = 0;

printf("Begin...\n");

for(int i = 0; i < 10; i++) /* C语言不允许在for语句中定义变量 */
{
for(int j = 0; j < 10; j++)
{
c += i * j;
}
}

printf("c = %d\n", c);

printf("End...\n");

printf("Press enter to continue ...");
getchar();

return 0;
}

注意:C语言中的变量都必须在作用域开始的位置定义!

2、register关键字的升级

a、register关键字请求“编译器”将局部变量存储于寄存器中,C语言中无法取得register变量地址。

b、在C++中依然支持register关键字。C++编译器有自己的优化方式,不使用register也可能做优化,C++中可以取得register变量的地址。

c、C++编译器发现程序中需要取register变量的地址时,register对变量的声明变得无效。

d、早期C语言编译器不会对代码进行优化,因此register变量是一个很好的补充。

#include <stdio.h>

int main(int argc, char *argv[])
{
register int c = 0;

printf("Begin...\n");

printf("&c = %08X\n", &c);

printf("End...\n");

printf("Press enter to continue ...");
getchar();

return 0;
}
3、全局变量的升级

a、在C语言中,重复定义多个同名的全局变量是合法的。
b、在C++中,不允许定义多个同名的全局变量。

注意:C语言中多个同名的全局变量最终会被链接到全局数据区的同一个地址空间上。而C++直接拒绝这种二义性的做法!

#include <stdio.h>

int g_var;
int g_var = 1; /* C++不允许多个同名全局变量 */

int main(int argc, char *argv[])
{
printf("Begin...\n");

printf("g_var = %d\n", g_var);

printf("End...\n");

printf("Press enter to continue ...");
getchar();

return 0;
}
4、const关键字的升级

a、当碰见常量声明时在符号表中放入常量。
b、编译过程中若发现使用常量则直接以符号表中的值替换。
c、编译过程中若发现对const使用了extern或者&操作符,则给对应的常量分配存储空间。

#include <stdio.h>

int main(int argc, char *argv[])
{
const int c = 0; /* const常量 */
int* p = (int*)&c; /* 对常量c分配了存储空间 */

printf("Begin...\n");

*p = 5; /* C++不允许const常量被修改 */

printf("c = %d\n", c);

printf("End...\n");

printf("Press enter to continue ...");
getchar();

return 0;
}

注意:在C语言中,const变量是只读变量,有自己的存储空间。而在C++中,可能分配存储空间。(当const常量为全局,并且需要在其它文件中使用。当使用&操作符取const常量的地址)

C++中的const常量类似于宏定义

const int c = 5;    /* #define c 5 */

C++中const常量与宏定义的区别:

a、const常量是由编译器处理的,提供类型检查和作用域检查。
b、宏定义由预处理器处理,单纯的文本替换。


5、struct关键字的升级

a、C语言的struct 定义了一组变量的集合,C编译器并不认为这是一种新的类型。
b、C++中的struct 是一个新类型的定义声明。

#include <stdio.h>

struct Student /* C语言中Student只是结构体名,而C++中Student已经被声明为一种新类型。 */
{
const char* name;
int age;
};

int main(int argc, char *argv[])
{
Student s1 = {"Delphi", 30};
Student s2 = {"Tang", 30};

printf("Press enter to continue ...");
getchar();

return 0;
}

6、变量和函数的类型

a、C++中所有的变量和函数都必须有类型。
b、C语言中的默认类型在C++中是不合法的。

那么int f(); 与 int f(void); 的区别是什么?

a、在C语言中
int f();表示返回值为int,接受任意参数的函数。
int f(void);表示返回值为int的无参函数。
b、在C++中
int f();和int f(void)具有相同的意义,都表示返回值为int的无参函数。

#include <stdio.h>

f(i) /* 没有指明函数返回类型 */
{
printf("i = %d\n", i);
}

g() /* 没有指明函数返回类型,并且调用g()函数的时候不能进行传参 */
{
return 5;
}

int main(int argc, char *argv[])
{
printf("Begin...\n");

f(10);

printf("g() = %d\n", g(1,2,3,4,5));

printf("End...\n");

printf("Press enter to continue ...");
getchar();

return 0;
}

7、bool类型

C++在C语言的基本类型系统之上增加了bool类型,bool可取的值只有true和false,并且只占用一个字节。

#include <stdio.h>

int main(int argc, char *argv[])
{
int a;
bool b = true;

printf("b = %d, sizeof(b) = %d\n", b, sizeof(b));

b = 3;
a = b;

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

printf("Press enter to continue ...");
getchar();

return 0;
}

注意:true代表真值,编译器内部用1来表示。false代表非真值,编译器内部用0来表示。

8、三目运算符的升级

a、C语言中的三目运算符返回的是变量值,不能作为左值使用。
b、C++中的三目运算符可直接返回变量本身,因此可以出现在程序的任何地方。

#include <stdio.h>

int main(int argc, char *argv[])
{
int a = 1;
int b = 2;

(a < b ? a : b) = 3;

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

printf("Press enter to continue ...");
getchar();

return 0;
}
注意:C++中三目运算符可能返回的值中如果有一个是常量值,则不能作为左值使用。

9、动态内存分配

a、C++中通过new关键字进行动态内存申请
b、C++中的动态内存申请是基于类型进行的
c、delete关键字用于内存释放

//变量申请:
Type* pointer = new Type;
// ……
delete pointer;

//数组申请:
Type* pointer = new Type[N];
// ……
delete[] pointer;
#include <stdio.h>int main(){    int* p = new int;        *p = 5;    *p = *p + 10;        printf("p = %p\n", p);    printf("*p = %d\n", *p);        delete p;        p = new int[10];        for(int i=0; i<10; i++)    {        p[i] = i + 1;                printf("p[%d] = %d\n", i, p[i]);    }        delete[] p;        printf("Press any key to continue...");    getchar();    return 0;}
new关键字与malloc函数的区别:
a、new关键字是C++的一部分,malloc是由C库提供的函数。
b、new以具体类型为单位进行内存分配,malloc只能以字节为单位进行内存分配。
c、new在申请单个类型变量时可进行初始化,malloc不具备内存初始化的特性。

10、引用与指针

10-1  引用

引用的定义:可以看作一个已定义变量的别名。
引用的语法:Type& name = var;

#include <stdio.h>

int main(int argc, char *argv[])
{
    int a = 4;
    int& b = a; /* b是a的别名,因此a和b是同一个单元 */
    
    b = 5;
    
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("&a = %08X\n", &a);
    printf("&b = %08X\n", &b);
    
    printf("Press enter to continue ...");
    getchar();
    
    return 0;
}

注意:定义引用的时候一定要进行初始化,指明该引用变量是谁的别名。但是引用作为函数参数声明时不进行初始化!

10-2  const引用

const引用的语法:const Type& name = var;
const引用的作用:让变量拥有只读属性。

#include <stdio.h>

int main(int argc, char *argv[])
{
int a = 4;
const int& b = a; /* const引用 */
int* p = (int*)&b; /* 只读变量可以通过指针修改值 */

//b = 5;

*p = 5;

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

printf("Press enter to continue ...");
getchar();

return 0;
}

当使用常量对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名。

10-3  引用的其它特性

引用在C++中的内部实现是一个常指针(Type& name  =  Type* const name)。

当函数返回值为引用时:
a、若返回栈变量,不能成为其它引用的初始值,不能作为左值使用。
b、若返回静态变量或全局变量,可以成为其他引用的初始值,即可作为右值使用,也可作为左值使用。

10-4  指针和引用的区别

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

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

c、引用一经初始化不能再引用其它变量,而指针可以修改。

11、函数的升级之内联函数inline

C++中推荐使用内联函数替代宏代码片段!

#include <stdio.h>

/* 宏定义的作用与内联函数类似 */
#define FUNC(a, b) ((a) < (b) ? (a) : (b))

inline int func(int a, int b)
{
return a < b ? a : b;
}

int main(int argc, char *argv[])
{
int a = 1;
int b = 3;
int c = func(++a, b);

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

printf("Press enter to continue ...");
getchar();

return 0;
}
注意:

a、内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求。

b、内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)。

c、内联函数只是请求C++编译器,但是C++编译器不一定准许函数的内联请求!

C++编译器提供了扩展语法,能够对函数进行强制内联。如:g++中的__attribute__((always_inline))属性

C++中内联编译的限制:
a、不能存在任何形式的循环语句
b、不能存在过多的条件判断语句
c、函数体不能过于庞大
d、不能对函数进行取址操作
e、函数内联声明必须在调用语句之前

12、函数的升级之函数默认参数

C++中可以在函数声明时为参数提供一个默认值,当函数调用时没有指定这个参数的值,编译器会自动用默认值代替。

#include <stdio.h>

int mul(int x = 3);

int main(int argc, char *argv[])
{
printf("mul(2) = %d\n", mul(2));
printf("mul(-2) = %d\n", mul(-2));
printf("mul() = %d\n", mul());

printf("Press enter to continue ...");
getchar();

return 0;
}

int mul(int x)
{
return x * x;
}
注意:

a、函数既有声明又有定义是,声明时指定了函数默认参数,定义的时候就不能再指定函数默认参数。

b、函数默认参数必须遵守从右至左的顺序,如果某个参数没有默认参数,那么它左边的参数就不能再有默认参数。

c、函数调用时,形参和实参按从左至右的顺序进行匹配。

13、函数的升级之占位参数

占位参数只有参数类型声明,而没有参数名声明。

#include <stdio.h>

int func(int a, int b, int = 0) /* 第3个参数为占位参数 */
{
return a + b;
}

int main(int argc, char *argv[])
{
printf("func(1, 2, 3) = %d\n", func(1, 2, 3));

printf("Press enter to continue ...");
getchar();

return 0;
}
它的意义在于兼容C语言程序中可能出现的不规范写法。

14、函数的升级之函数重载

相同的作用域,如果两个函数名称相同,而参数不同,我们把它们称为重载overload。
重载的条件:(至少满足下述一个条件)

a、形参数量不同

b、形参类型不同

c、形参顺序不同

但是函数返回值不能作为重载条件!

#include <stdio.h>
#include <string.h>

int func(int x)
{
return x;
}

int func(int a, int b)
{
return a + b;
}

int func(const char* s)
{
return strlen(s);
}

int main(int argc, char *argv[])
{
int c = 0;

c = func(1);

printf("c = %d\n", c);

c = func(1, 2);

printf("c = %d\n", c);

c = func("12345");

printf("c = %d\n", c);

printf("Press enter to continue ...");
getchar();

return 0;
}
调用重载函数时,编译器通过检查实际参数的个数、类型和顺序来确定相应被调用的函数!
15、函数的升级之C与C++混合编程

在项目中融合C++和C代码可能是实际工程中不可避免的。

#ifdef __cplusplus
extern "C" {
#endif

int func(int a, int b)
{
return a + b;
}

#ifdef __cplusplus
}
#endif
16、命名空间

在C语言中只有一个全局作用域,C语言中所有的全局标识符共享同一个作用域,标识符之间可能发生冲突。

因此C++提出了命名空间的概念,目的是将全局作用域分成不同的部分,不同命名空间中的标识符可以同名而不会发生冲突。

C++命名空间的定义:namespace name { /* … */ }

#include <stdio.h>

namespace First
{
int i = 0;
}

namespace Second
{
int i = 1;

namespace Internal
{
struct P
{
int x;
int y;
};
}
}

int main()
{
using namespace First;
using Second::Internal::P;

printf("i = %d\n", i);
printf("i = %d\n", Second::i);

P p = {2, 3};

printf("p.x = %d\n", p.x);
printf("p.y = %d\n", p.y);

printf("Press any key to continue...");
getchar();
return 0;
}

C++命名空间的使用: 

a、使用整个命名空间:using namespace name;
b、使用命名空间中的变量:using name::variable;
c、使用默认命名空间中的变量:::variable
17、类型转换

a、static_cast强制类型转换

- 用于基本类型间的转换,但不能用于基本类型指针间的转换。
- 用于有继承关系类对象之间的转换和类指针之间的转换。

b、const_cast强制类型转换

- 用于去除变量的const属性。

c、reinterpret_cast强制类型转换

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

d、dynamic_cast强制类型转换

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

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