前言
c++是在c语言的基础上,增加了面向对象编程、引用、函数重载、模板库STL等新特性,使得c++成为一门功能强大、灵活多变的语言。c++在语法上兼容大部分c语言,因而学习了c语言之后,会对c++学习有一定的帮助。相比java,c++语法的学习难度较高,但是它难学易用,也有利于我们理解底层,是一门十分值得深入学习的语言。
接下来我们会重点介绍一些c++的前置基础知识,便于我们快速入门c++语法。
一、手搓一个Hello World
那么就让我们从HelloWorld开始,进入c++的知识海洋。代码如下:
//c语言版本 int main() { printf("Hello World\n"); return 0; }
//c++版本 using namespace std; int main() { cout << "Hello World" << endl; return 0; }
由于c++兼容c语言语法,所以这两段代码在c++编译器中都是可以完成运行的。
运行结果:
可以看到,c++的基本语法和c语言还是有较大区别的。看不懂没关系,我们将会逐一讲解这里的细节。
二、命名空间namespace
在c++当中,由于变量、函数、类等数量庞大,难免会出现重名的情况,它们都存在与全局域当中,使用时就会出现冲突。而命名空间的出现就解决了这个问题。命名空间会对标识符的名称进行本地化,本质上是使它们位于不同的作用域中,避免了冲突的情况。
接下来我们尝试定义命名空间。
1.命名空间的定义
举个例子:
namespace xxx { int x = 5; int func(int a) { return a * a; } struct A { int m; char n; }; }
1.定义命名空间使用的关键字是namespace,后面加上该空间的名字,在之后的 { } 中定义变量、函数或类等等。
2.命名空间只能定义在全局,不能定义在函数体或者类中。
3.命名空间可以嵌套定义。
4.一个项目的多文件中定义的同名命名空间,编译器会认为是同一个命名空间,不会发生冲突。
2.命名空间的使用
接下来,我们尝试访问命名空间中的成员。
namespace xxx { int x = 5; int func(int a) { return a * a; } struct A { int m; char n; }; } int main() { int x = 10; printf("%d\n", x + xxx::x);//访问命名空间的成员时,在空间名之后加上两个冒号,称之为域限定操作符 printf("%d\n", xxx::func(x)); return 0; }
运行结果:
使用using关键字还可以将命名空间或者其成员展开。举例:
namespace a { int m = 5; int n = 3; } namespace b { int p = 10; int q = 20; } using namespace a;//展开整个命名空间 using b::p;//展开命名空间的某成员 int main() { printf("m=%d,n=%d\n", m, n); printf("p=%d,q=%d\n", p, b::q); return 0; }
运行结果:
可以看到,展开命名空间或者成员之后,在访问时就不需要再加上“::”了。这里需要注意:在大型项目当中尽量不要展开命名空间,很容易发生冲突的情况,日常练习时为了方便可以使用。
我们之前的HelloWorld代码中,使用了语句“using namespace std;”展开了命名空间std。
3.命名空间补充知识
1.c++标准库都放在叫做“std”的命名空间当中。
2.namespace本质上就是定义了一个域,叫做命名空间域。
3.c++中有四种域:函数局部域、全局域、命名空间域、类域;不同的域当中的相同变量或者函数名形成域隔离,不会冲突;函数局部域和全局域会影响变量的声明周期,命名空间域和类域不会影响变量声明周期。
三、c++中的输入和输出
接下来,我们按照刚才写的HelloWorld程序介绍c++的输入输出。
using namespace std; int main() { cout << "Hello World" << endl; return 0; }
1.可以看到,我们引入了头文件"iostream",它是c++的标准输入、输出流库,定义了标准的输入输出对象。
2.cout,也就是std::cout,是类ostream的对象,它主要面向窄字符的标准输出流,与c语言中printf函数作用相似,与printf不同的是,它可以自动识别要输出的变量的类型,在使用时不需要特别指定输出类型。
3.endl:是一个函数,它用于输出一个换行符,与传统的输出“\n”不同的是,它可以在各种操作系统下正常使用,而“\n”在不同的操作系统中含义可能不同。
4.我们看到,“Hello World”字符串被两个“<<”符号围了起来,这个符号叫做“流插入操作符”,它可以理解为将该操作符之后的内容插入到"cout"中便于输出。当我们需要输出多个变量时,可以将这些变量全部用“<<”分隔开。
对于输入操作,c++提供了"std::cin",它是类istream的对象,主要面向窄字符的标准输入流。在使用它时,我们需要加上“>>”(流提取操作符),可以理解为将输入的值提取到变量当中。
接下来我们写一段代码测试c++中的输入输出:
using namespace std; int main() { int a = 3; float b = 5.5f; char c = 'w'; cout << a << ' ' << b << ' ' << c << endl;//不同的内容之间必须用<<分割 int d = 0; cin >> d; cout << d; return 0; }
运行结果:
四、缺省参数
缺省参数(默认参数),指的是在声明或者定义函数时,可以给函数的参数设置一个默认值。当调用该函数时,如果没有传对应参数,则使用该默认值;否则使用传入的参数。
1.缺省参数可分为全缺省参数和半缺省参数,全缺省参数指的就是函数的参数全部设置了默认值,半缺省参数指的就是部分参数设置了默认值。C++标准规定:半缺省参数默认值的设置必须按照函数参数从右往左进行,不能跳跃。代码示例:
using namespace std; void func1(int a = 3)//全缺省参数 { cout << a << endl; } void func2(int a, int b = 0)//半缺省参数 { cout << a + b << endl; } int main() { func1(); func1(1); func2(1); func2(1, 2); return 0; }
运行结果:
以下情况运行就会报错:
using namespace std; void func3(int a = 10, int b)//缺省参数只能从右往左设置 { cout << a + b << endl; } int main() { func3(3, 5); return 0; }
2.调用带缺省参数的函数时,实参的传入必须从左到右进行,不能跳跃。代码示例:
using namespace std; void func(int a, int b = 3, int c = 5) { cout << a + b + c << endl; } int main() { func(1, , 1);//报错 return 0; }
3.当函数的声明和定义分离时,缺省参数不能同时出现在声明和定义当中,必须在声明中设置缺省参数。
五、函数重载
c++中,当同一作用域中出现同名函数时,如果这些函数的形参不同(参数个数不同或者参数类型有不同),就会出现函数重载,这些函数之间不会发生冲突情况。相比c语言,c++中函数重载的出现,体现了多态性,使得函数使用更加灵活。
举个例子:
using namespace std; int add(int a, int b)//两函数的参数类型不同,出现重载 { return a + b; } double add(double a, double b)//两函数的参数类型不同,出现重载 { return a + b; } int main() { cout << add(1, 2) << endl; cout << add(3.3, 5.5) << endl; return 0; }
运行结果:
可以看到,编译器会根据我们调用函数时传入的参数类型,来决定调用哪一个重载函数。
下面来看一个特殊情况:
void func() { cout << 1 << endl; } void func(int a = 10) { cout << 2 << endl; } int main() { func();//报错 }
两个func函数构成函数重载,但是当调用函数时不传参,就会出现歧义,编译器无法确定我们调用的是哪一个函数。
六、内联函数
相比c语言,c++引入了“内联函数”这个概念,它对程序的效率提升有一定帮助。接下来我们深入了解以下内联函数。
1.用关键字“inline”修饰的函数叫做内联函数,在程序编译的过程中,编译器会在调用该函数的地方将此函数展开,这样在程序运行时就不会创建函数栈帧,提高了效率。
2.由于函数的体量有别,所以并不是所有用“inline”修饰的函数都会在编译时展开,使用“inline”修饰只是程序员的建议,最终是否展开由编译器决定。一般代码量较短的函数会被展开,而代码量较大或者递归函数就不会被展开,展开之后反而会增加程序冗余。
3.当一个函数被我们使用“inline”修饰时,如果该函数的声明和定义是分离的,那么将会导致编译错误。所以使用“inline”修饰函数时要同时进行声明和定义。
七、空指针
在c语言中,空指针用“NULL”来表示,它是一个宏常量,是被强制类型转换为void型指针的0;而c++中的“NULL”直接替换为0。由于c++中存在函数重载,当我们将NULL作为参数传递时,可能会出现以下情况:
using namespace std; void func(int* ptr) { cout << 1 << endl; } void func(int x) { cout << 2 << endl; } int main() { func(NULL); return 0; }
运行结果:
我们传入空指针,本意是要调用第一个函数,但是结果却调用了第二个函数。如果我们传入被强制转换为void*的0呢?
可以看到,程序出现了报错,我们仍然无法调用第一个函数。针对这种问题,c++定义了一个关键字来表示空指针:nullptr。它是一种特殊类型的字面量,可以转换为任意类型的指针。由于它只能被转换为指针类型,所以就避免了以上问题。我们来传入nullptr试试:
using namespace std; void func(int* ptr) { cout << 1 << endl; } void func(int x) { cout << 2 << endl; } int main() { func(nullptr); return 0; }
运行结果:
可以看到,程序成功地调用了第一个函数。
总结
今天我们学习了关于c++的一些前置知识,这些新的概念和定义有效地弥补了c语言的一些不足。之后我们的c++程序都会以这些知识为基础。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤