我是靠谱客的博主 怕孤单世界,最近开发中收集的这篇文章主要介绍【C++】-- 命名空间、函数重载、内联函数一、命名空间二、C++输入输出 三、缺省参数四、函数重载 五、内联函数六、指针空值nullptr ,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

一、命名空间

1.命名空间的定义

2.命名空间的3种使用方式

二、C++输入输出

 三、缺省参数

1.缺省参数定义

2.缺省参数分类

四、函数重载

1.函数重载概念

2.c++支持重载的原因

3.C++中将一个函数按照C的风格来编译 

 五、内联函数

1.定义

2. 特性

3.为什么不能每个函数都使用inline 

4.inline和宏

六、指针空值nullptr 


 到c++板块啦

一、命名空间

C++中存在大量的变量、函数和类,都存在于全局作用域中,会导致冲突。使用命名空间可以对标识符的名称进行本地化,来避免命名冲突或名字污染,达到名称隔离的目的。

1.命名空间的定义

C++使用namespace关键字定义命名空间,用来解决命名冲突问题,格式为:namespace后面跟命名空间名字,再接一对{ } ,{ }中是命名空间的成员。

(1)命名空间格式:

namespace N1 //N1是命名空间的名称
{
	//命名空间的内容,既可以定义变量,也可以定义函数
	int a = 0;

	int Add(int left, int right)
	{
		return left + right;
	}
}

(2)命名空间可嵌套:

namespace N2 //N2命名空间中嵌套了N3命名空间
{
	int a = 0;
	int b = 0;

	int Add(int left, int right)
	{
		return left + right;
	}

	namespace N3
	{
		int c;
		int d;

		int sub(int left, int right)
		{
			return left - right;
		}
	}
}

(3)同一个工程中允许存在多个同名命名空间,编译器会合成到同一个命名空间中,如:

001-test.cpp中存在N1命名空间

“::”操作符是域解析操作符

#include<iostream>
using namespace std;
namespace N1 //N1是命名空间的名称
{
	//命名空间的内容,既可以定义变量,也可以定义函数
	int e;

	int Add(int left, int right)
	{
		return left + right;
	}
}

int main()
{
	cout <<"e="<< N1::e << endl;//endl是全局换行符
    return 0;
}

 002-test.cpp中也存在N1命名空间

#include<iostream>
namespace N1 //N2命名空间中嵌套了N3命名空间
{
	int b = 0;
	int c = 8;

	namespace N3
	{
		int c;
		int d;

		int sub(int left, int right)
		{
			return left - right;
		}
	}
}

 

注意:

(1)同一命名空间不能定义相同的变量名和函数名。

(2)一个命名空间定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。

(3)C++为了防止命名冲突,把自己库里面的东西都定义在std命名空间中。

2.命名空间的3种使用方式

(1) 加命名空间名称及作用域限定符(N::a),比较麻烦,每个用到域中成员的地方都得指定成员的域

#include<iostream>
using namespace std;

namespace N
{
	int a = 0;

	int Add(int left, int right)
	{
		return left + right;
	}
	int Sub(int left, int right)
	{
		return left - right;
	}

	namespace N1  //定义嵌套命名空间
	{
		int max(int left, int right)
		{
			return left > right ? left : right;
		}
	}
}

int main()
{
	cout << "a = " << N::a << endl;

    //嵌套命名空间的使用
	int b = N::N1::max(3, 4);
	cout << "b = " << b << endl;

    return 0;
}

(2)是用using将命名空间成员或函数引入(using N::a,访问a时就不需要加访问限定符"::")

#include<iostream>
using namespace std;

namespace N
{
	int a = 0;

	int Add(int left, int right)
	{
		return left + right;
	}
	int Sub(int left, int right)
	{
		return left - right;
	}
}

using N::a;

int main()
{
	cout << "a = " << a << endl;
    return 0;
}

(3)使用using namespace 命名空间名称引入(using namespace N,访问a时就不需要加访问限定符"::")

#include<iostream>
using namespace std;

namespace N
{
	int a = 0;

	int Add(int left, int right)
	{
		return left + right;
	}
	int Sub(int left, int right)
	{
		return left - right;
	}
}

using namespace N;

int main()
{
	cout << "a = " << a << endl;
    return 0;
}

对于std命名空间,如果使用using namespace 命名空间名称将std整个引入,那么库里面的东西全都被展开包含到全局了,虽然看起来方便,但是如果自己定义的内容和库冲突了,就没法解决了。所以日常练习可以使用using namespace std的方式引入std命名空间,规范的工程项目中不推荐。

using namespace std;

 可以展开常用的命名空间,下面代码只用到打印,只展开cout和endl命名空间来代替展开std:

#include<iostream>
using  std::cout;
using  std::endl;

namespace N
{
	int a = 0;

	int Add(int left, int right)
	{
		return left + right;
	}
	int Sub(int left, int right)
	{
		return left - right;
	}
}

using N::a;

int main()
{
	cout << "a = " << a << endl;
    return 0;
}

 使用cout和endl时,也可以不使用using nampspace 命名空间,直接指定成员所在的域

#include<iostream>

namespace N
{
	int a = 0;

	int Add(int left, int right)
	{
		return left + right;
	}
	int Sub(int left, int right)
	{
		return left - right;
	}
}

using N::a;

int main()
{
	std::cout << "a = " << a << std::endl;
	return 0;
}

二、C++输入输出

 1.使用cout标准输出(控制台)和cin标准输入(键盘)时,必须要包含< iostream >头文件及std标准命名空间:

#include<iostream>
using namespace std;

2.c++可以自动识别类型,不需要增加数据格式控制。C语言必须要指定数据格式:整型--%d,字符--%c。

#include<iostream>
using namespace std;

int main()
{
    int a;
    double b;
    char c;

    cin >> a;
    cin >> b >> c;

    cout << a << endl;
    cout << b << " " << c << endl;

    return 0;
}

 三、缺省参数

1.缺省参数定义

缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用给定的实参。

#include<iostream>
using namespace std;

void TestFunc(int a = 0)
{
	cout << a << endl;
}

int main()
{
	TestFunc();    //未给定实参
	TestFunc(10);  //给定实参
	return 0;
}

2.缺省参数分类

(1)全缺省参数:在函数声明或定义时为所有参数都指定一个默认值,调用函数时只给传了实参的参数赋值为实参,否则为默认值:

#include<iostream>
using namespace std;

void TestFunc(int a = 0, int b = 10, int c = 20)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;

	cout << "n" << endl;
}

int main()
{
	TestFunc(3);
	TestFunc(3,4);
	TestFunc(3, 4, 5);

	return 0;
}

(2)半缺省参数:在函数声明或定义时从右向左为部分参数指定一个默认值,且不能间隔给出

#include<iostream>
using namespace std;

//void TestFunc(int a = 10, int b, int c = 20)间隔给出默认值,错误
//void TestFunc(int a = 10, int b, int c)从左向右给出默认值,错误
void TestFunc(int a, int b = 10, int c = 20)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;

	cout << "n" << endl;
}

int main()
{
	TestFunc(3);
	TestFunc(3,4);
	TestFunc(3, 4, 5);

	return 0;
}

注意:

(1)缺省参数不能在函数声明和定义中同时出现。

(2)缺省值必须是常量或者全局变量

(3)C语言不支持缺省(编译器不支持)

四、函数重载

c语言不允许定义同名函数,但是c++能够通过函数重载定义同名函数 

1.函数重载概念

C++中重载函数通常用来命名一组功能相似而数据类型不同的函数:函数重载是指在同一作用域内,一组函数名相同,不同参数列表(不同参数个数 或 不同类型 或 不同顺序)的函数,这组函数被称为重载函数。

函数重载作用:减少了函数名的数量,避免了名字空间的污染,增加了程序可读性。

如下3个Add函数构成重载,它们的参数类型都不相同,参数类型分别是int、double、long

#include<iostream>

int Add(int left, int right)
{
	return left + right;
}

double Add(double left, double right)
{
	return left + right;
}

long Add(long left, long right)
{
	return left + right;
}

int main()
{
	Add(10, 20);
	Add(10.3, 20.5);
	Add(10L, 20L);

	return 0;
}

注意1:函数是否重载取决于同名函数参数列表是否相同,函数重载和函数返回值类型没有关系。

如下两个函数不是函数重载,编译会报错,因为参数列表相同,不构成重载,尽管返回值类型不同,但函数重载定义并不关注返回值类型。

int Add(short left, short right)
{
	return left + right;
}

short Add(short left, short right)
{
	return left + right;
}

注意2:带缺省参数的函数和原函数不算重载

 如下两个函数参数类型、个数、顺序都相同,不构成重载

int Add(int left, int right)
{
	return left + right;
}

int Add(int left = 2, int right = 3)
{
	return left + right;
}

 另外,虽然下面的两个f函数构成重载,但是调用时报错,因为16行对f函数进行调用时,编译器并不知道是该匹配两个参数的f函数还是该匹配带有缺省参数c的f函数,因此会报错。

void f(int a, int b, int c=1)
{

}

void f(int a, int b)
{

}

int main()
{
	f(1, 2);
	f(1, 2, 3);

	return 0;
}

2.c++支持重载的原因

 c/c++编译过程:

在linux环境中,创建f.h文件,写如下代码:  

 创建f.cpp文件,写如下代码:

创建main.c文件,写如下代码:

 执行g++ -o cpp f.cpp main.cpp

 编译通过,生成cpp可执行文件

执行objdump -S cpp

 生成汇编代码:

 

 main.cpp的#include "f.h"包含了f.h文件,f.h文件只是包含了两个add函数的声明,在编译阶段,编译器会认为函数定义在其他地方,而让函数编译通过,等到链接时,再找函数的定义。

链接时,通过函数名即函数地址去其他目标文件中找add的地址,如果找到了就填上add的地址。

根据c++函数名修饰规则,只要参数不同,修饰出来的函数名就不同,这就支持了重载:把参数类型首字母带进函数名中,参数不同,函数签名就不同。

_Z3addii函数名中,_Z是前缀,3是函数的字符长度(add长度为3),ii是两个参数类型int的首字母

同理,_Z3adddd函数名中,_Z是前缀,3是函数的字符长度(add长度为3),dd是两个参数类型double的首字母。

以上也能看出c++函数签名和返回值类型无关。

那么,对于c语言直接拿函数名做名称,无法支持定义两个同名函数

f.h

 f.c

main.c

执行 执行g++ -o test f.cpp main.cpp

编译通过,生成test可执行文件 

 执行objdump -S test

 生成汇编代码:

C语言无法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,这就支持了重载。

3.C++中将一个函数按照C的风格来编译 

使用extern "C"可以在C++文件中引用c文件的函数

f.h文件

#pragma once
#define  _CRT_SECURE_NO_WARNINGS  1
#include<stdio.h>

int add(int a, int b); 

f.c文件

#include "f.h"

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

 main.cpp

#include "f.h"

int main()
{
	int ret = add(2, 3);
    printf("%d",ret);

	return 0;
}

程序报错,这是因为当c++调用add函数时,add函数被编译器编译后,函数签名变成了"?add@@YAHHH@Z"链接时,main函数却找不到这个符号。  

为了能够让c++调用c文件中实现的函数,需要给该函数加上extern "C",修改f.h文件如下:

#pragma once
#define  _CRT_SECURE_NO_WARNINGS  1
#include<stdio.h>

//写法一
extern "C" int add(int a, int b);

//写法二,能针对多行声明:可将要按照c风格进行编译的所有函数声明全部放在{ }中
//#ifdef __cplusplus
//extern "C"{
//#endif
//int add(int a, int b);
//#ifdef __cplusplus
//};
//#endif

 编译成功

为什么加上extern "C"后,就能执行成功呢?

这是由于windows下的编译器会在c语言符号(变量和函数)前加上“_”,即add函数被编译后的符号为_add。因为c语言如果只有函数声明而缺少函数定义,会报缺少"_add"符号:

f.h

#pragma once
#define  _CRT_SECURE_NO_WARNINGS  1
#include<stdio.h>

//extern "C" int add(int a, int b);
#ifdef __cplusplus
	extern "C"
#endif
int add(int a, int b);

f.c

#include "f.h"

//int add(int a, int b)
//{
//	return a + b;
//}

 main.c

#include "f.h"

int main()
{
	printf("%d", add(2, 3));
	return 0;
}

在VS下,c++文件调用c实现的函数时,编译时,c函数add编译后的符号为"_add"。而在c++的main中,add编译后的符号为"?add@@YAHHH@Z"。两个符号不一致,导致链接失败。而当add在extern "C"中声明后,c++的main中就会将add编译成符号"_add",符号一致,链接成功。

 五、内联函数

1.定义

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,因此,内联函数能够提升程序运行的效率。 

#include<iostream>

int Add(int a, int b)
{
	int c = a + b;
	return c;
}

int main()
{
	int ret = 0;
	ret = Add(1, 2);

	return 0;
}

VS环境下,F10-调试-窗口-反汇编,发现会call Add

加上inline关键字后,在release模式和debug模式下查看是否存在call Add语句

(1)release模式下,直接查看编译器生成的汇编代码中是否存在call Add

(2)debug模式下,编译器默认不会对代码进行优化,需要对编译器进行设置,否则不会展开:调试-调试属性-配置属性-C/C++-常规-调试信息格式-程序数据库(/Zi)

调试-调试属性-配置属性-C/C++-常规-内联函数扩展-只适用于_inline(/Ob1)

查看反汇编结果,没有call Add,直接使用了add指令:

2. 特性

(1)inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。 

(2)inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。

(3)inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到

f.c 

#pragma once
#include <iostream>
using namespace std;
inline void f(int i);

 f.cpp

#include "f.h"

void f(int i)
{
	cout << i << endl;
}

main.cpp

#include "f.h"

int main()
{
	f(10);
	return 0;
}

 编译报错,链接错误,如下所示。将inline修饰的函数声明和定义放在.h或源文件就可以了。

3.为什么不能每个函数都使用inline 

假如有一个函数,进行编译汇编后有100条指令,如果有10个地方调用

(1)该函数不加inline,建立栈帧,总计100 + 10 = 110条指令

(2)该函数加inline,不建立栈帧,每个地方都展开,总计100 * 10 = 1000条指令

从110条指令变成1000条指令,虽然建立栈帧不一定比不建立栈帧慢,但是编译出来的可执行程序变大,安装软件的人体验变差,执行程序内存消耗变多。

4.inline和宏

C语言为了避免小函数建帧的消耗,提供宏函数支持,在预处理阶段展开。既然C语言已经解决了,为什么C++还要提供inline函数?

因为宏有以下缺点:

(1)不支持调试(编译阶段进行了替换)

(2)宏函数语法复杂,容易出错

(3)没有类型安全检查

c++使用三种法师代替宏:枚举、const常量定义、inline内联函数。

六、指针空值nullptr 

NULL实际是一个宏,NULL的定义: 

NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。但对于如下代码:

void f(int)
{
    cout<<"f(int)"<<endl;
}

void f(int*)
{
    cout<<"f(int*)"<<endl;
}

int main()
{
    f(NULL);
    f((int*)NULL);

    return 0;
}

在c++中,NULL被定义为0,想用f(NULL)用空指针NULL作为参数,但是根据打印结果,发现 f(NULL)调用的是参数为int的f函数:

这是因为C++中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。 为此,C++11新增了关键字nullptr,用于表示空指针。为向后兼容,C++11仍然可以使用0来表示空指针,因此表达式nullptr=0为true,但使用nullptr提供了更高的类型安全。

注意:

1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。

2.nullptr是指针类型,不能转化为整形类型,可以隐式转换为任意类型的指针,也可以隐式转换为bool类型代表false。

3. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。 

4. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

最后

以上就是怕孤单世界为你收集整理的【C++】-- 命名空间、函数重载、内联函数一、命名空间二、C++输入输出 三、缺省参数四、函数重载 五、内联函数六、指针空值nullptr 的全部内容,希望文章能够帮你解决【C++】-- 命名空间、函数重载、内联函数一、命名空间二、C++输入输出 三、缺省参数四、函数重载 五、内联函数六、指针空值nullptr 所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部