一、C++的源起始末
在谈C++之前,C语言是一个无论如何都不能绕开的话题。C语言是大家在初次接触编程时最常见的语言,而且使用最为普遍。然而在我们学习的后期,随着我们遇到的问题的复杂度上升,C语言的局限和短板已经不利于我们去解决规模较大的问题了。
因此,在20世纪80年代,通过对C语言进行补强,一种全新的语言C++出现了。C++不但兼容C语言,可以进行C语言条件下的程序编写,而且既可以以抽象数据为特点进行基于对象的程序设计,又能够进行面向对象程序设计,在很大程度上补齐了C语言只能解决小规模问题的短板。
那么接下来,就随笔者一起学习这门复杂难精但趣味横生的语言吧!
二、推开C++的大门
(一)命名空间
在C语言中,我们知道有很多变量和函数是在内部早就定义好的,存在于一定的作用范围。因此有一个比较常见的错误就是在程序中出现变量存在命名冲突。如下代码所示:
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
printf("%d\n", rand);
return 0;
}
这是一个简单C语言程序,但是一运行就必定报错。因为在stdlib.h的头文件中包含了rand()函数,这就造成了编译报错。
我们都知道,在C语言以及C++中,存在着大量以上情况的变量、函数以及之后会学到的类。它们都普遍存在于全局作用域中,而且一个差错就会导致编译不通过。在C语言的框架下无法解决这样的问题,于是C++就提出了命名空间来处理,来避免命名冲突或名字污染。
在C++中设计了namespace关键字,用于命名空间的定义。命名空间的定义也十分简单,只需要在namespace后面跟命名空间的名字,再接上{}即可,{}中就是该命名空间的成员。
namespace ywd //命名空间+名字
{
int rand = 10; //命名空间的成员
}
这样就解决了命名冲突的问题了。既然命名空间定义结束,接下来就需要探讨命名空间的用法了。
命名空间的三种用法
1.使用作用域限定符
如果命名空间定义结束后直接运行如下代码,结果依旧是报错:
#include <stdio.h>
namespace ywd
{
int a = 10;
}
int main()
{
printf("%d\n", a);
return 0;
}
既然已经定义了一个命名空间,我们想使用该命名空间的成员,就需要用到作用域限定符 :: 来调用命名空间内的成员。
#include <stdio.h>
namespace ywd
{
int a = 10;
}
int main()
{
printf("%d\n", ywd::a); //命名空间名 + :: +空间成员名
return 0;
}
2.使用using展开命名空间成员
如果该命名空间中的某一成员调动频繁,我们可以使用using来展开该成员,使其作用于全局域中,不必使用作用域限定符。
#include <stdio.h>
namespace ywd
{
int a = 10;
int b = 1;
}
using ywd::a; //使用using展开成员a
int main()
{
printf("%d\n", a); //无需作用域限定符就可以调用a
printf("%d\n", ywd::b); //成员b没有被展开
return 0;
}
3.使用using展开整个命名空间
using除了可以展开命名空间内的某个成员,也可以将整个命名空间展开。一般在做大型项目时不建议展开整个命名空间(不论是自己定义的命名空间还是标准命名空间),容易导致空间内成员与其它变量等产生命名冲突或名字污染,所以很少会使用第三种方法。
#include <stdio.h>
namespace ywd
{
int a = 10;
int b = 1;
}
using namespace ywd; //using + namespace + 命名空间名
int main()
{
printf("%d\n", a); //可以直接调用空间内成员
printf("%d\n", b);
return 0;
}
(二)输入和输出
C++虽然脱胎于C语言,但在一些基础语法上也大相径庭。其中最为典型的例子就是输入和输出语句。C语言中我们主要使用printf语句和scanf语句进行输入输出,而C++的输入输出语句如下代码所示:
#include <iostream>
//std:C++标准库的命名空间
using namespace std;
int main()
{
// << 流插入运算符
cout << "hello,world!"<<endl; //endl表示换行输出
int i = 0;
// >> 流提取运算符
cin >> i;
//cin和cout可以自动识别类型
return 0;
}
C++使用cout标准输出对象(控制台)和cin标准输入对象(键盘),且必须在使用时包含头文件<iostream>和使用标准命名空间。从代码中明显看出,C++的输入输出方式较C语言更为方便,不必手动控制格式。
(三)缺省参数
缺省参数是在定义或声明时为函数的参数提供一个缺省值,在调用该函数时,如果没有指定的实参,就采用形参的缺省值,否则就使用指定实参。比如在C语言程序中,我们初始化一个栈时,并不知道需要开辟多少空间才不会造成短缺或浪费。如果在C++程序中,我们可以指定一个缺省值,在初始化时就不必考虑空间的大小问题,后续也可以对其进行修改,并且C语言程序不可以使用缺省值。
#include <iostream>
using namespace std;
//缺省参数
void func(int a = 10)
{
cout << "a = " << a << endl << endl;
}
int main()
{
func(); //没有传入实参时,使用形参的缺省值
func(1); //有传入实参时,使用指定的实参
return 0;
}
缺省参数的两种类
1.全缺省参数
顾名思义,即函数的所有形参都有缺省值。
//全缺省参数
void func(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
int main()
{
func(1, 2); //a = 1, b = 2, c = 30
func(1, 2, 3); //a = 1, b = 2, c = 3
return 0;
}
2.半缺省参数
不用顾名思义,指的是函数只有部分形参有缺省值。
//半缺省参数
void func(int a, int b = 20, int c = 30) //只要a没有缺省值
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
int main()
{
func(1, 2); //a = 1, b = 2, c = 30
func(1, 2, 3); //a = 1, b = 2, c = 3
return 0;
}
关于缺省参数的注意事项:
● 半缺省值必须从右往左依次赋予,且不能出现间隔。
//错误示例
int func(int a = 10, int b = 20, int c) //不能从左往右,只能从右往左
int func(int a = 10, int b, int c = 30) //不能出现间隔,必须依次赋值
● 缺省值不能在函数声明和定义中同时出现。
缺省值如果同时出现在声明和定义中,且提供的缺省值各不同,则编译器无法确定使用哪一个缺 省值。
● 缺省值必须是常量或全局变量。
(四)函数重载
函数重载是函数的一种特殊情况,C++允许在同一作用域中声明数个功能相似的同名函数。这些函数通常形参列表不同,大多数是为了处理功能相近但数据类型不同的情况。一般根据形参列表的不同又可以分为三类。
函数重载的三种类
1.参数类型不同
#include <iostream>
using namespace std;
//1.参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
int main()
{
Add(1, 2);
Add(1.1, 2.1);
return 0;
}
2.参数个数不同
#include <iostream>
using namespace std;
//2.参数个数不同
void Func()
{
cout << "Func()" << endl;
}
void Func(int a)
{
cout << "Func(int a)" << endl;
}
int main()
{
Func();
Func(1);
return 0;
}
3.参数类型顺序不同
#include <iostream>
using namespace std;
//3.参数类型顺序不同
void Two(int a, char b)
{
cout << "void Two(int a, char b)" << endl;
}
void Two(char b, int a)
{
cout << "void Two(char b, int a)" << endl;
}
int main()
{
Two(1, 'a');
Two('a', 1);
return 0;
}
C++支持函数重载的原因
和C语言相比,C++的函数重载是相当便捷的。那为什么C语言不支持函数重载,C++却支持呢?这就涉及C/C++程序运行的过程。
在C/C++中,运行一个程序需要经过预处理、编译、汇编和链接这四个阶段。我们的源文件在汇编过程中会分别处理出主函数和函数的符号表。在链接阶段会合并符号表。在C语言中,如果函数出现重复定义的话,在编译过程中就会出现重定义报错。因为C语言程序编译时,不会对函数名进行修饰,如果出现重名就会引发歧义,导致函数调用出错。而在C++中,程序在编译时会按照特定的函数修饰规则对函数名进行修饰,两个功能不同的函数即使函数名相同,但修饰之后就可以对二者做出区分。
三、结语
以上就是本文的全部内容,但这只是C++入门的部分知识,在之后笔者会进行后续介绍和讲解,和大家一起继续深入学习与探讨C++的无穷奥妙。感谢观看!