C到C++——C++基础

C++是一种通用的、静态类型的、跨平台的编程语言。它是在1979年由Bjarne Stroustrup创建的,最初是作为C语言的扩展来支持面向对象编程。

C++在保留C语言的特性的同时,添加了许多其他的功能,包括类、对象、继承、多态、模板等。这使得C++成为了一种强大而灵活的编程语言,可以用于开发各种类型的应用程序,包括系统级软件、嵌入式系统、游戏开发、图形界面等。

C++也是一种高效的语言,其执行速度接近于C语言,因为它允许直接访问硬件和内存,并支持底层的操作。

C++还具有丰富的标准库,提供了许多常用的函数和数据结构,可以方便地进行文件操作、输入输出、字符串处理等。

总的来说,C++是一种功能强大、高效、灵活的编程语言,广泛应用于软件开发领域。

在系统的了解C++之前,我们要了解一些C++基础,以便我们后面更好的理解其他内容。大家也可以借助这一篇来了解C++对C有何变化:C++对C的扩充-CSDN博客

一.命名空间

1.1同名冲突

当我们在编写程序的时候,如果在一个作用域中出现了两个或多个同名的变量或者函数就会发生错误

int main()
{
	int a = 0;

	//......

	int a = 0;
	return 0;
}

我们在主函数这个作用域中定义了两个a变量,在编译的时候就会报错。这就是同名冲突

同名冲突是在大型应用软件的设计过程中是非常容易发生的。比如程序员A在头文件codeA中定义了student类和fun函数,程序员B在头文件codeB中也定义了student类和fun函数。而当主程序员使用A、B写的功能时要包含其头文件,但是当包含之后,就会在编译时发生错误,因为头文件在编译时会展开,这就意味着在一个程序中同时出现了两个studen类和fun函数,此时就会导致编译失败,因为发生了同名冲突。

1.2命名空间

为了解决这个问题,ANSIC++增加了命名空间(namespace)。所谓命名空间,实际上就是一个由程序设计者命名的内存区域。程序设计者可以根据需要指定一些有名字的空间作用域,把一些全局实体分别放在各个命名空间中,从而与其他全局变量实体分割开来。

命名空间的定义格式为:

namespace 命名空间名
{
	//命名空间成员
}

如:定义一个名为xiasicheng的命名空间

namespace xiasicheng
{
	//命名空间成员
	const int a = 10;

	int a = 2;

	int func()
	{
		cout << "hello" << endl;
	}

	struct student
	{
		int age;
		char name[10];
	};


}

命名空间的作用时建立一些互相分隔的作用域,把一些全局实体分隔开来,以免产生名字冲突。

在声明一个命名空间时花括号内不仅可以可以包含变量,还可以包含一下类型:

  • 变量(可以初始化)

  • 常量

  • 函数(可以是定义也可以是声明)

  • 结构体

  • 模板

  • 命名空间(命名空间的嵌套)

    namespace xiasicheng
    {
    //命名空间成员
    const int a = 10;

      namespace xiasicheng2
      {
      	int b = 0;
      }
    

    }

需要注意的是,命名空间不会改变变量的生命周期,本来是全局变量,定义在命名空间中还是全局变量。

1.3命名空间的使用

1.3.1指定命名空间访问

当我们需要使用命名空间中的成员时,要用命名空间名和作用于限定符对命名空间成员进行限定,以区别不同的命名空间中可能有同名的标识符:即

//命名空间名::命名空间中的成员名
cout << xiasicheng::a << endl;

但是当命名空间名字比较长,并且还要多次引用其成员时,就会非常不方便。

为此,C++提供了一些机制,能简化使用命名空间中成员的方法

1.3.2使用命名空间的别名

可以为命名空间起一个别名,用来代替较长的命名空间名。

如:

namespace Television
{
	int a = 0;
}

namespace TV = Television;//用TV代替Television

int main()
{
	cout << Television::a << endl;
	cout << TV::a << endl;

	return 0;
}

1.3.3使用using将命名空间中某个成员展开

using后面的成员名必须是由命名空间名限定的名字。例如:

using TV::a;

它表示在using语句所作用的作用域中可以用TV命名空间中的成员a,而且不必再逐个用命名空间名限定了。当我们要再使用变量a时,直接调用即可:

cout << a << endl;//相当于cout << TV::a << endl;

但是要注意,在同一作用域中用using声明的不同命名空间的成员不能有同名的成员。

1.3.4使用using namespace展开命名空间

C++提供了using namespace 语句来实现一次性展开命名空间,一般格式为:

using namespace 命名空间名;

当我们利用该语句展开某个命名空间之后,该命名空间中的所有成员都可以直接进行访问,不必再使用作用于限定符限定或者using语句声明某一个成员。
当是在使用using namespace 和using展开某一个成员时,都存在风险,所以在项目中时,建议使用命名空间名+作用于限定符的方式来访问变量。

二.C++的输入输出

C++有一套自己的输入输出方式,我们可以使用cout来实现输出,cin实现输入。它们是C++的标准输出流和输入流。

int a = 0;
cin >> a;
cout << a << endl;

因为C++是包含C的,所以在我们的C++程序中,也可以使用C语言的输入输出方式,但是C++的输入输出有一个很好的东西,那就是可以自定识别类型。我们不再像C语言那样要进行格式控制,我们C++直接写入变量名即可,不必控制变量的类型,cout和cin会自动识别的。

int main()
{
	int a = 0;

	cin >> a;
	scanf("%d", &a);

	cout << a << endl;
	printf("%d\n", a);

	return 0;
}

其实cout和cin也是包含在std这个命名空间中的,因为我们再文件开头就展开了std,所以我们不再需要进行限定,所以cout和cin的使用可以借助std和作用域限定符:

std::cin >> a;
std::cout << a << endl;

三.缺省参数

缺省参数也叫做有默认参数的函数,所以缺省参数是应用在函数模块中的。其含义就是在定义函数时,给形参一个默认值,当我们调用该函数时,如果没有传实参,该函数就会使用这个默认值,如果传了实参,该函数就会使用该实参而屏蔽掉缺省参数。

int MAX(int a = 0, int b = 0)
{
	return a > b ? a : b;
}

当一个函数有多个形参时,其可以分为全缺省,半缺省,不缺省:

全缺省就是其形参每一个都带有默认值,传参数时,可以只传一个,也可以传多个,传了就用实参,没传的就用缺省值。

半缺省就是只有一部分形参有缺省值,当一个函数是半缺省时,其带有缺省值的形参必须在形参表列的最右边。

int func(int a, int b, int c = 0, int d = 0)
{
	cout << a << " " << b << " " << c << " " << d << endl;
}

如果缺省值跳跃,在传参时就会产生歧义:

int func(int a = 1, int b, int c = 0, int d = 0)
{
	cout << a << " " << b << " " << c << " " << d << endl;
}

int main()
{
	func(2);
	return 0;
}

此时,2是传给a呢?还是b呢?所以为了避免该情况的出现,带缺省值的形参应在形参表列的最右边。

四.变量的引用

引用(reference)是C++对C的一个重要扩充

4.1引用的概念

在C++中,变量的"引用"就是变量的别名,因此引用又叫别名(alias)。建立"引用"的作用是为一个变量再起另一个名字,以便在需要时可以方便、间接地引用该变量,这就是引用名称的由来。当我们给一个变量起了一个别名之后,他们两个占用同一块空间。

int a = 10;
int& b = a;//给a取了一个别名b,a和b占用同一块内存空间

注意:在声明一个别名时,必须同时给出初始化。

我们打印a和b的地址可以看到它们的地址相同。 当我们修改其中任意一方时,另一方也会发生改变。

4.2引用的特性

4.2.1引用在定义时必须初始化且引用的类型必须和被引用对象的类型相同

int main()
{
	int a = 0;
	int& b = a;//正确

	int& c;//没有初始化

	double& d = a;//引用类型与被引用类型不同

	return 0;
}

4.2.2一个变量可以有多个引用

int a = 0;
int& b = a;
int& c = a;
int& d = a;

当我们打印它们的地址时,它们都指向的是同一块空间:

4.2.3引用在初始化后不能再被重新声明为另一个变量的别名

int main()
{
	int a = 0;
	int& b = a;//b是a的别名

	int c = 10;
	&b = c;//企图让b成为c的别名,错误

	return 0;
}

4.2.4对于引用的初始化,可以用一个变量名,也可以用另一个引用

int main()
{
	int a = 10;
	int& b = a;
	int& c = a;//用变量初始化

	int& d = b;//用另一个引用初始化
	return 0;
}

4.3引用作为函数参数

用引用作为函数参数时,可以代替传地址的方式使实参改变,当函数形参为引用时,实参直接传变量也会改变。

void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

我们可以对比引用传参和指针传参的区别,引用传参会更加方便,如果使用指针的话还要对变量进行解引用。

4.4const引用

const引用可以作为普通变量的别名,也可以作为const变量的别名。前者属于权限缩小,或者属于权限平移。

int a = 0;
const int& b = a;

此时b不可以改变,但是a可以改变。 此时属于权限缩小,原来a可写可读,b只能读不能写。

const int c = 100;
const int& d = c;

此时c和d都不能改变。此时属于权限平移,原来c只可读不可写,d也只能读不能写。

需要注意的是,权限只能平移或者缩小,但不能放大。

const int e = -1;
int& f = e;

e只可读,但f作为e的引用可读可写,此时权限放大,运行会出错。

4.5const引用中的常属性临时变量

double a = 1.2;
int& b = a;

a是浮点型,而想给其取一个int型的别名。此时会发生隐式转化,a会隐式转化为整型储存在临时变量中,而临时变量具有常属性,只可读,但是给其取别名时发生了权限放大,所以上面的写法是错误的。

正确的方法是用const引用接收:

double c = 2.2;
const int& d = c;

4.6引用和指针的关系

C++中引用是引用,指针是指针,两者相辅相成。

  • 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
  • 引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
  • 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。
  • 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。
  • sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下 占4个字节,64位下是8byte)。
  • 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。

五.重载函数

C++中的输入输出语句中用到的<<和>>叫做流插入操作符和流提取操作符。而在C语言中,它们作为位运算符,这叫做运算符重载,即一物多用。

而重载函数也叫函数重载,也就是一物多用的意思,同名函数有着不同的作用。

C++允许在同一作用域中用同一函数名定义多个函数,这些函数的参数个数和参数类型不相同,用来实现不同的功能。这就是函数重载,即一个函数名多用

int ADD(int x, int y)
{
	return x + y;
}

double ADD(double x, double y)
{
	return x + y;
}

对上面两个函数而言,实现了整数相加和浮点数相加的功能,当我们调用ADD函数时,该函数会根据参数的不同,调用不同的函数。这就是函数重载的体现。

但是函数重载的前提是:函数的形参表列不同

  • 形参个数不同
  • 形参类型不同
  • 形参个数和类型都不同
  • 形参顺序不同

需要注意的是,函数重载跟返回值类型无关,返回值不同,形参相同的两个函数不能设计为重载函数。

六.inline

inlne是C++的一个关键字,它可以使一个函数成为内联函数。

调用函数时需要建立函数栈帧,这需要时间,当一个函数被频繁调用时,就会加大时间的消耗,导致程序效率降低。C++提供了一种提高效率的方法,即在编译时将所调用函数的代码嵌入到主调函数中。这种嵌入到主调函数中的函数称为内联函数,又称内置函数。

inline int ADD(int x, int y)
{
	return x + y;
}

当我们调用该函数时,其就会在调用出展开:

int main()
{
	ADD(1, 2);

	return 0;
}
//展开
int main()
{
	return 1 + 2;

	return 0;
}

其实,inline的出现是为了替代C语言中的宏。因为宏是一种文本替换不进行类型检查,不安全。所以C++就引入了inline这一关键字。

但是函数是否展开是跟编译器有关的。当你将一个函数定义为inline时,编译器会检查你设置的是否合理,如果合理,编译器就会在调用出展开;否则,就不展开。
将一个函数定为内联函数的前提是,该函数代码量少(10行之内),且会被频繁调用。

七.nullptr

nullptr是C++11引入的一个空指针常量。在C++中,空指针通常用NULL或0来表示,但这种表示方式可能存在一些模糊性和歧义。为了解决这个问题,C++11引入了nullptr作为一个明确的空指针常量。

nullptr可以被赋值给指针类型的变量,表示该指针不指向任何有效的内存地址。使用nullptr可以提高代码的可读性和安全性,因为它可以更清晰地表示一个空指针。另外,nullptr还可以与其他指针进行比较操作,以判断指针是否为空。

示例代码:

cpp 复制代码
int* ptr = nullptr; // 定义一个指向整型的空指针

if (ptr == nullptr) {
    // 检查指针是否为空
    // 执行相关操作
}

总的来说,nullptr是C++中用于表示空指针的常量,它在C++11中被引入,提高了代码的可读性和安全性。


完!

相关推荐
我们的五年21 分钟前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
做人不要太理性1 小时前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.1 小时前
2、桥接模式
c++·桥接模式
chnming19871 小时前
STL关联式容器之map
开发语言·c++
程序伍六七1 小时前
day16
开发语言·c++
小陈phd2 小时前
Vscode LinuxC++环境配置
linux·c++·vscode
火山口车神丶2 小时前
某车企ASW面试笔试题
c++·matlab
是阿建吖!2 小时前
【优选算法】二分查找
c++·算法
Ajiang28247353044 小时前
对于C++中stack和queue的认识以及priority_queue的模拟实现
开发语言·c++
‘’林花谢了春红‘’9 小时前
C++ list (链表)容器
c++·链表·list