IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 针对C的不足而改进的C++基础语法 -> 正文阅读

[C++知识库]针对C的不足而改进的C++基础语法

命名空间

全局变量,函数,类都是存在于全局作用域的,随着项目规模变大和分工细化,这类变量,函数,类等等的命名冲突将不可避免,C无法解决这类问题,有鉴于此,C++提出命名空间的概念,目的就是将标识符名称本地化,以解决命名冲突或者命名污染。

#include <stdio.h>
#include <math.h>
//fmax是库函数,全局变量fmax与其重名
//error C2365: “fmax”: 重定义;以前的定义是“函数”

int fmax = 10;
int main()
{
	printf("%d\n", fmax);
	return 0;
}

?命名空间的定义

namespace 标识符 {}

例如:

a. 变量

#include <stdio.h>
#include <math.h>
//注意此处fmax仍然是全局变量
//全局变量和局部变量根本区别在于存储于内存中不同的区
namespace cal
{
	int fmax = 10;
}
int main()
{
	printf("%p\n", fmax);
    printf("%d\n", cal::fmax);
	return 0;
}

第一个输出的值其实是库函数fmax的地址,函数名就是函数的地址;第二个值因为指定了是命名空间cal里面的fmax,所以输出10。

b. 函数

#include <stdio.h>

namespace cal
{
	int Add(int x, int y)
	{
		return x + y;
	}
}
int main()
{
	printf("%d\n", cal::Add(1, 2));
	return 0;
}

c. 类型

#include <stdio.h>

namespace cal
{
	struct Node
	{
		int val;
		struct Node* next;
	};
}
int main()
{
	struct cal::Node newNode;//注意struct要放在前面
	cal::struct Node test;//err
	return 0;
}

::叫做域限定符?。

命名空间不影响变量的作用域,它只是限定一个域,就好像建起一个围墙,里面可以用本地化的名字,而且它影响编译器的查找规则。

编译器的查找规则:先在局部找,局部找不到再到全局去找,并且不会去命名空间里找。如果变量被域限定符修饰,则编译器就直接去指定的域里查找,找不到就报错,不会再去局部或者全局查找。

域限定符前面如果为空,则表明限定的是全局域。

命名空间可以嵌套

#include <stdio.h>

namespace N1
{
	int a = 0;
	int b = 1;

	namespace N2
	{
		int c = 3;
		int d = 4;
	}

}
int main()
{
	printf("%d\n", N1::a);
	printf("%d\n", N1::N2::c);
	return 0;
}

N2嵌套于N1中,在使用里面的变量时要递推指定。要注意,a,b,c,d都是全局变量,作用域是定义位置到文件结尾。

std是c++官方库文件的命名空间?

同一个项目中同级同名的命名空间会被自动合并

命名空间的3种使用方式

1. 使用空间名+域限定符::的方式,如以上例子。

2. 使用using namespace 空间名 的方式。

#include <iostream>
using namespace std;
int main()
{
	cout << "hello" << endl;
	return 0;
}

这种方式会让命名空间展开,相当于拆掉围墙,好处是使用里面的变量方便,坏处是变量暴露在外,可能会冲突,平时练习可以,项目中不会这么用。

3. 使用部分展开的方式。

#include <iostream>
using std::cout;

//cout被展开,endl没有展开
int main()
{
	cout << "hello" << std::endl;
	return 0;
}

?可以把常用的展开,自己定义的时候避免和常用的重名即可。

C++的输入&输出

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

2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。

3. <<是流插入运算符,>>是流提取运算符

4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。
与C相比,C++的输入输出可以自动识别变量类型。

5. 格式化输入输出的话还是printf和scanf比较方便。

缺省参数

缺省参数的概念

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

#include <iostream>
using namespace std;

void Func(int a = 1, int b = 2, int c = 3)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
int main()
{
	Func(10, 20, 30);
	Func(10, 20);
	Func(10);
	Func();
    Func(10, ,20);//err,不支持
	return 0;
}

实参和形参从左往右依次匹配?

形参的缺省参数从右到左要连续缺省,中间不能断开,这种情况叫半缺省。

#include <iostream>
using namespace std;

void Func(int a , int b = 2, int c = 3)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
int main()
{
	Func(10, 20, 30);
	Func(10, 20);
	Func(10);
	Func();//error C2660: “Func”: 函数不接受 0 个参数
	return 0;
}

应用举例:

#include <iostream>
using namespace std;
namespace project
{
	typedef struct stack
	{
		int* arr;
		int top;
		int capacity;
	}stack;

	void stackInit(stack* st, int defaultCapacity = 4)
	{
		st->arr = (int*)malloc(sizeof(int) * defaultCapacity);
		st->top = 0;
		st->capacity = defaultCapacity;
	}
}

int main()
{
	project::stack s1;
	//project::stackInit(&s1, 100);//此处较为灵活
	project::stackInit(&s1);
	return 0;
}

?注意:缺省参数必须是常量或者全局变量

? ? ? ? ? ? 缺省参数不能在函数的声明和定义中同时出现

#include <iostream>
using namespace std;
int defaultNum = 100;
//error C2587: “a”: 非法将局部变量作为默认参数
void Func(int a=defaultNum , int b = a, int c = 3)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
int main()
{
	Func(10, 20, 30);
	Func(10, 20);
	Func(10);
	Func();
	return 0;
}
#include <iostream>

int Add(int a = 1, int b = 2)
{
	return a + b;
}
int main()
{
	int ret = Add(10, 20);
	return 0;
}
int Add(int a = 1, int b = 2);// error C2572: “Add”: 重定义默认参数 : 参数 1

函数重载?

?概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这 些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同。

//在C语言中,Add函数如果需要支持不同类型的数据相加
//就需要定义不同名字的函数,比如AddInt,AddDouble
//C++则不需要,可以只使用一个名字Add
#include <iostream>	
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(1.4, 3.9) << endl;
	return 0;
}

注意:函数同时具有默认参数和函数重载时,可能语法正确,调用时会出错:

#include <iostream>	
using namespace std;

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

int Add()
{
	return 100;
}


int main()
{
	cout << Add() << endl;// error C2668: “Add”: 对重载函数的调用不明确
	cout << Add(1.4, 3.9) << endl;
	return 0;
}

?函数重载的实现是编译器通过对函数名修饰而实现的,修饰规则并不包括返回值,所以返回值不能作为重载的区分条件,但为什么不修饰返回值,是因为函数调用的时候不能指定返回值,所以无法用返回值区分。

内联函数

定义:C++中以inline修饰的函数叫内联函数,编译时编译器会在调用的地方展开,省去了创建堆栈的开销,提升运行效率,适用于频繁调用的小函数。

在C中,解决的方案是使用宏函数。但是,使用宏有缺点:

1. 在预处理阶段已经展开,所以不支持调试。

2. 没有类型检查。

3. 容易写错。

针对C中宏的缺点,C++用const和enum来代替宏常量,推出内联函数来代替宏函数。

注:在VS下,默认情况下debug版本下inline不会展开,因为需要调试。可以通过设置让VS在debug下展开inline函数。

#include <iostream>
using namespace std;
inline int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int ret = 0;
	ret = Add(1, 2);
	cout << ret << endl;
	return 0;
}

从下面的汇编指令可以清楚看出,使用内联函数后,call消失了,Add函数的功能被直接用几条add指令代替:

?

?但是内联函数也有自身的限制:

1. inline对于编译器只是建议,最终还是由编译器来决定是否使用内联。

当函数体代码行数较多,函数体里面使用递归等等情况下,编译器不会展开函数,否则会引发代码膨胀,因为编译好的代码长度影响可执行程序/静态库/动态库的大小。

2. inline不支持函数声明和定义分离,因为inline函数被展开后就没有了函数地址,链接时编译器将找不到这个地址,无法完成链接。

error LNK2019: 无法解析的外部符号 "int __cdecl Add(int,int)" (?Add@@YAHHH@Z),函数 _main 中引用了该符号

编译器在看到inline时,函数名不会被放入符号表,所以链接时就会找不到这个函数。

C++11中的auto关键字

早期C/C++中auto关键字是用来定义具有自动存储类型的局部变量。

C++11中,auto不再表示自动存储类型,而是作为一个新的类型指示符告诉编译器,auto定义的变量的类型必须在编译阶段推导得出。

#include <iostream>
using namespace std;
struct Person
{
	char* name;
	int age;
};
int main()
{
	int ia = 10;
	double id = 12.5;
	char ic = 'A';
	auto iia = ia;
	auto iid = id;
	auto iic = ic;
	cout << typeid(iia).name() << endl;
	cout << typeid(iid).name() << endl;
	cout << typeid(iic).name() << endl;
	
	auto pia = &ia;
	auto pid = &id;
	auto pic = &ic;
	cout << typeid(pia).name() << endl;
	cout << typeid(pid).name() << endl;
	cout << typeid(pic).name() << endl;

	auto con_ia = 1;
	auto con_id = 3.14;
	auto con_ic = 'y';
	cout << typeid(con_ia).name() << endl;
	cout << typeid(con_id).name() << endl;
	cout << typeid(con_ic).name() << endl;

	struct Person p={nullptr, 23};
	auto pp = p;
	cout<<typeid(pp).name()<<endl;


	//auto e;//error C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
	int test;
	//auto my_test = test;// error C4700: 使用了未初始化的局部变量“test”
	int t = 0;
	auto& rt = t;//指定推导为引用时,auto后面需要加&来告诉编译器。
	cout << typeid(rt).name() << endl;//rt类型是int

	const int con_a = 100;
	auto aa = con_a;
	cout << typeid(aa).name() << endl;

    //auto e = 10, f = 20.0;// error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
	return 0;
}

注意:

1. auto 定义的变量必须初始化,因为auto其实是一个类型占位符,编译期间必须要被替换成一个确定的具体类型。

2. 同一行可以用auto定义多个变量,但这些变量的类型必须相同。

3.?用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须 加&。

另外,还有一些场景不能用auto来推导:

1. 定义函数形参时

auto作为函数形参,编译时编译器无法得知形参的大小,也就无法确定需要开辟的栈帧的大小。

2. 不能用来声明数组?

应用:

#include <iostream>
using namespace std;
int main()
{
	int arr[] = { 1,2,3,4,5 };
	for (auto e : arr)
	{
		cout << e << endl;
	}
	
	for (auto& e : arr)//注意此处要用auto&才能实现写入数组,否则e只是数组内容的拷贝
	{
		e *= 2;
	}
	for (auto e : arr)
	{
		cout << e << endl;
	}
	return 0;
}

for循环迭代的范围必须是确定的?

void TestFor(int array[]) 
{
    for(auto& e : array)
        cout<< e <<endl;
}

上面这个编译出错,因为数组作为形参传递的是指针,不能确定begin和end的位置

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:33:07  更:2022-09-30 00:34:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 4:01:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码