全局变量,函数,类都是存在于全局作用域的,随着项目规模变大和分工细化,这类变量,函数,类等等的命名冲突将不可避免,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的位置
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)