针对C的不足而改进的C++基础语法

针对C的不足而改进的C++基础语法,第1张

命名空间

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

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

int fmax = 10;
int main()
{
	printf("%d\n", fmax);
	return 0;
}
 命名空间的定义

namespace 标识符 {}

例如:

a. 变量

#include 
#include 
//注意此处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 

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

c. 类型

#include 

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

::叫做域限定符 。

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

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

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

命名空间可以嵌套

#include 

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 
using namespace std;
int main()
{
	cout << "hello" << endl;
	return 0;
}

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

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

#include 
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 
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 
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 
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 
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 

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 	
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 	
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 
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 
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 = ⁣
	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<

注意:

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

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

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

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

1. 定义函数形参时

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

2. 不能用来声明数组 

应用:

#include 
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 <

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/3002836.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)