使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
namespace N1
{
int a;
int Add(int left, int right)
{
return left + right;
}
}
namespace N2
{
int a;
int b;
int Add(int left, int right)
{
return left + right;
}
namespace N3
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
同一个工程中允许存在多个相同名称的命名空间
编译器最后会合成同一个命名空间中(可以写两个N1)
namespace N1
{
int Mul(int left, int right)
{
return left * right;
}
}
输入输出 cin和cout都在std这个命名空间内
#include
using namespace std;
int main()
{
//1.std::不用using namespace std endl换行
std::cout << "hello world"<> i >> d;
//2.加了using namespace std 不需要std::
cout << i << " " << d << endl;
return 0;
}
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。
例如:
#include
using namespace std;
void Func(int a = 0)
{
cout << a << endl;
}
int main()
{
Func(10);//10
Func();//0
}
#include
using namespace std;
//全缺省 所有参数都给
//可以传1个 也可以传多个
void Func1(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
//半缺省 给部分参数
//必须传1个(没有缺省的位置必须传) 也可以多个
//必须从右往左连续缺省 右边的必须先给值
void Func2(int a, int b = 10, int c = 20)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
int main()
{
Func1();//10 20 30
Func1(1);//1 20 30
Func1(1, 2);//1 2 30
Func1(1, 2, 3);//1 2 3
Func2(1);//1 10 20
Func2(1, 2);//1 2 20
Func2(1, 2, 3);//1 2 3
}
注意:只有返回类型不同不能构成重载 必须有参数不同
void Func()
int Func()
short Add(short left, short right)
{
return left+right;
}
int Add(short left, short right)
{
return left+right;
}
都不构成函数重载
为什么C语言不支持函数重载 而C++支持呢?
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
编译链接过程
编译这一步时
原因
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern "C",意思是告诉编译器,将该函数按照C语言规则来编译。
void TestFunc(int a = 10)
{
cout<<"void TestFunc(int)"<
不形成函数重载 因为两个函数的参数相同
C语言没办法支持重载,因为同名函数没办法区分。(编译器在编译.c文件时,只会给函数进行简单的重命名)
C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
在.cpp文件中,虽然两个函数的函数名一样,但是他们在符号表中生成的名称不一样。
可以 在函数前加extern "C"
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
#include
using namespace std;
int main()
{
int a = 1;
int& ra = a;//ra是a的引用 引用也就是别名 a再取了一个名称ra
int& b = a;
int& c = b;
//4个名字公用一块空间
//改变任何一个别名的值 每个别名和变量本身都会改变
c = 10;
//10 10 10 10
cout << a << " " << ra << " " << b << " " << c << " " << endl;
}
注意:引用类型必须和引用实体是同种类型的
#include
using namespace std;
int main()
{
int a = 1;
//int& b; //引用必须在定义时初始化
int& c = a;
int d = 2;
c = d;
//分析:这里是c变成了d的引用? no
//还是d赋值给c? ok
//说明c已经是a的引用了 不会再是其他变量的引用
//结论:引用一旦引用一个实体,再不能引用其他实体
return 0;
}
#include
using namespace std;
int main()
{
//int a = 0;
//int& b = a;//b的类型是int
//如果
//const int a = 0;
//int& b = a;//b的类型是int 编译不通过 原因:a是只读 b的类型是int 可读 可写 所以不行
const int a = 0;
const int& b = a;
int c = 1;
int& d = c;
const int& e = c;
//行不行?可以的-> C可读可写 e变成别名只读
//总结:引用取别名时 变量访问的权限可以缩小 不能放大
//取别名变量范围小
return 0;
}
别名是可读可写 如果变量只可读 那么取别名编译失败
别名的权限较小
进阶:隐式类型转换+取别名
#include
using namespace std;
int main()
{
int i = 0;
double d = i;//隐式类型转换
//double& rb = i;
//float& rf = i;
//跟字节大小无关
//无法通过编译
//但加const 可以
const double& rd = i;
const float& rf = i;
//为什么?
//i->double临时变量->db/rd/rf
//临时变量具有常性(const只可读)
//所以不加const就放大了权限 加const就可以成立
//变量之间赋值没有权限缩小和放大的关系 引用才有
const int ci = i;
int x = ci;
return 0;
}
一个int变量给到一个double变量
int变量->double临时变量(临时变量具有常性)->取别名的时候只能是const类型的
权限不能放大
都可以 没有权限问题
void swap_c(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//r1是a的别名 r2是b的别名 r1和r2的交换就是a和b的交换
void swap_cpp(int& r1, int& r2)
{
int tmp = r1;
r1 = r2;
r2 = tmp;
}
#include
using namespace std;
//返回一个临时变量(tmp) tmp的类型是int tmp的值是n的值
//这里会多产生一个tmp的空间
int Count1()
{
static int n = 0;
n++;
return n;
}
//这个时候tmp就是n的别名 返回的就是n
//这里不会产生多的空间
int& Count2()
{
static int n = 0;
n++;
return n;
}
int main()
{
//int& r1 = Count1();编译不通过 返回的是tmp(临时变量) 临时变量具有常性 只可读 给到可读可写 不行
const int& r1 = Count1();
int& r2 = Count2();//返回的就是tmp(n的别名) 不是临时变量
return 0;
}
但引用返回就规避了问题 因为引用和原本变量是同一空间 可以理解就是返回原来的变量
注意:
int& Add(int a, int b)
{
int c = a + b;
return c;
//局部变量(栈上) 返回后 栈销毁了
//返回c的别名
}
int main()
{
//返回之后用了c的别名 再取一个别名ret
//栈已经销毁还给系统 还去使用这块空间 会出现随机值
int& ret = Add(1, 2);
cout << "Add(1, 2) is :" << ret << endl;
return 0;
}
此时c是引用返回 但c是局部变量(栈上) 出了Add函数作用域后 栈销毁了 还给了系统
此时还去使用这块空间 出现随机值
解决方法:
注意当使用static时(那条语句只会被执行一次):
int& Add(int a, int b)
{
static int c = a + b;
//这条语句只会被执行一次(第二次及以后调用函数不会再次执行)
//只会被定义一次变量
return c;
}
int main()
{
int& ret1 = Add(1, 2);
int& ret2 = Add(3, 4);//就算再调用一次 c还是3
cout << "Add(1, 2) is :" << ret1 << endl;//3
cout << "Add(3, 4) is :" << ret2 << endl;//3
return 0;
}
如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回
什么变量可以使用引用返回:全局变量 静态变量
那么函数使用引用返回好处是什么?->少开辟一个拷贝变量的空间 提高程序的效率
作参数时
#include
#include
using namespace std;
struct A
{
int a[10000];
};
void TestFunc1(A a)
{}
void TestFunc2(A& a)
{}
void TestRefAndValue()
{
A a;
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
int main()
{
TestRefAndValue();
return 0;
}
作为返回类型
#include
#include
using namespace std;
struct A
{
int a[10000];
};
//40000byte
//值返回
A TestFunc1(A a)
{
return a;
}
//引用返回
A& TestFunc2(A& a)
{
return a;
}
void TestRefAndValue()
{
A a;
//以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
//以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
//分别计算两个函数运行结束后的时间
cout << "TestFunc1(A) time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&) time:" << end2 - begin2 << endl;
}
int main()
{
TestRefAndValue();
return 0;
}
初始化 用法 sizeof +- 安全等方面
不同:
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
#include
using namespace std;
//在前面加inline(内联) 空间换时间
//原:Call Swap(1w)+3行指令
//现:Call Swap(1w*3) 直接全部展开了 空间变大了 但时间减少了
inline void Swap(int& x1, int& x2)
{
int tmp = x1;
x1 = x2;
x2 = tmp;
}
//频繁调用Swap 调用函数需要建立栈帧 是有消耗的
//如何解决:1.C语言使用宏函数 2.C++使用内联函数
int main()
{
int a = 0;
int b = 2;
Swap(a, b);
return 0;
}
宏的优缺点?
优点:
缺点:
C++有哪些技术替代宏?
C++11:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
int a = 0;
auto b = a;
此时b的类型就会根据a的类型推导出来
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
#include
using namespace std;
int main()
{
int a = 0;
auto b = a;//b的类型是根据a的类型推导出是int
int& c = a;
auto& d = a;//引用类型加上&
auto* e = &a;
auto f = &a;//不加*也可以推出来指针 指针可以不加
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
cout << typeid(e).name() << endl;
cout << typeid(f).name() << endl;
//auto实际作用
//类型很长可以直接写成auto
//使用auto来进行优化 简化代码的方法
return 0;
}
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
auto a=1,b=2;
auto c=3,d=4.0;
第二行编译失败 因为auto已经是int类型了 auto d = 4.0不可以
参数和数组不能用auto
auto不能作为形参类型
auto不能直接用来声明函数
C++98:for(int i = 0;i C++11:for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。 注意: for循环迭代的范围必须是确定的 void TestFor(int array[]) { for (auto& e : array)//这里传参 array是首元素地址 无法通过编译 cout << e << endl; } 这里是首元素地址 for循环范围不确定 C++98: int* p = NULL; NULL实际是一个宏 stddef.h中: #ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。 字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量。 但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。 这里就会遇到麻烦 你想的是指针 但编译器会默认为整形常量 所以C++11引用nullptr关键字 【C++】1.C++基础 完#include
8.指针空值nullptr(C++11)