一 默认参数
1、什么是默认参数
在定义函数时,可以在形参后面加上赋值;表示这个形参有一个默认值,如果调用函数时不传实参,则形参取默认值;
#include <iostream>
using namespace std;
int add(int a = 1, int b = 2)
{
return a+b;
}
int main()
{
cout << add() << endl;;
return 0;
}
结果:
3
2、注意点
(1)传参时顺序为从左往右,因此默认参数设置必须从右往左;
传参顺序为:若有实参则从第一个形参开始赋值,挨着赋值直到实参轮完;才会开始使用默认参数;如果形参列表中左边有默认值,最右边没有,而实参个数比形参少,那么最右边的形参没有传递值,则编译不通过;
(2)声明中写了默认参数,定义时不需要重复写;
(3)默认参数不能是引用;因为引用在定义后不能再改变引用对象;
(4)默认参数和函数重载一起使用,可能产生二义性问题,多加小心;
二 new & delete
C++引入这两个运算符用于动态申请和释放内存;
大致理解一下
new是一个运算符,后跟一个数据类型就能在堆上申请这个类型大小的空间并返回这块空间的首地址,返回的就是这个类型的指针;
delete是匹配的运算符,后跟new出来的指针,就能释放new出来的内存空间;
1、怎么用
new的基本用法
【1】类型 * 指针名 = new 类型
【2】类型 * 指针名 = new 类型(初值)
【3】类型 * 指针名 = new 类型 [个数]
第一种是申请一个类型大小空间,(基本类型不初始化,类的话会调用构造函数),(如果类的构造函数有形参,则应该使用【2】),返回首地址;
第二种是申请一个类型大小空间,(并用初值初始化),然后返回首地址;
第三种是申请[个数]个类型大小空间,返回首地址;(如果是基本类型如数组,不会初始化,而如果是类,则会调用构造函数);
简单讲就是()初始化;[]写数量;那么这俩能不能同时用呢?比如new(2)[3]或者new[3] (2);答案是不能,编译不通过,只能选【2】或者【3】中的一种,所以对于构造函数有形参的类,只能用【2】一个一个的申请;总结一下:
(1)对于类空间的申请,如果构造函数无形参,则可以用【1】或者【3】,如果有形参,则必须用()而且必须写实参,这意味着一次只能申请一个类对象空间,即只能【2】;
(2)对于基本类型如int,【1】【2】【3】都可以,需要说明的是:当用【3】时,可以在[个数]后跟(),但不能写实参,这表示讲这片空间都初始化为0;如int *p=new int2;则*p=0; *(p+1)=0;这不是【2】和【3】的混用;
哦,当然,有可能申请空间失败,则会返回一个NULL;但这种情况很少见;可以注意到new到的空间没有变量名,只能通过指针访问;
deltete的基本用法
【1】delete p;用于释放单变量地址空间;
【2】delete [] p;用于释放数组变量空间,[]内不写东西;
如果new的类型是类,则会调用析构函数;
基本注意点:
1、指针的类型必须是能指向new后所跟的类型单元;
int *p = new int;
int *p = new int [10];
int (*p)[10] = new int [3][10];
int (*p)[2][32] = new int [3][2][32];
2、new出的必须delete,如new出的类空间在堆上,其析构函数直到程序结束都不会自动调用,只能由delete来调用;
3、new出的空间只能通过指针访问;因此不能对这个指针进行自增等改值操作,这会导致程序找不到正确的空间地址;容易导致释放错误的空间或者无法释放堆区空间,即内存泄漏;
4、delete时不要造成重复释放,即对一个指针释放两次,很可能两次delete之间指针已经指向了其他有效空间;
5、delete只会释放空间,不会删除指针,也不会更改指针的指向,因此delete之后应该做置空,防止误访问;
int *p = new int[2];
cout << p << endl;
*p = 1;
*(p+1) = 2;
cout << *p << "," << *(p+1) << endl;
delete [] p;
//释放空间后再次访问
cout << *p << "," << *(p+1) << endl;
cout << p << endl;
结果:
0xec7128
1,2
15466688,15466688
0xec7128
2、深入一点
请看这位大佬的文章:c++ new的用法(总结)
三 命名空间
1、为什么要使用命名空间/什么是命名空间
2、怎么定义
2.1 namesapce + 命名空间名称 +{ 包含的内容 };简单来说,{}内啥都能放;
//定义一个名字为A的命名空间(变量、函数)
namespace A
{
int a = 100;
}
namespace B
{
int a = 200;
}
void test02()
{
//A::a a是属于A中
cout<<"A中a = "<<A::a<<endl;//100
cout<<"B中a = "<<B::a<<endl;//200
}
2.2 命名空间只能在全局范围内或者命名空间内定义中定义,即只能嵌套定义而不能在局部范围内定义;
//必须放在全局范围,即所有{}的外面;否则编译不通过;
namespace a
{
int r;
namespace b //可以嵌套定义
{
int v;
}
}
int add()
{
namespace c{}//错误,编译时报错;
}
那么嵌套定义的命名空间怎么用呢?很简单,既然b在a中,那么b的使用就和其他名称成员一样,加上作用域a::即可,可以理解为”嵌套使用“;
int main()
{
a::r=0;
a::b::v=1;
cout<<a::r<<endl;
cout<<a::b::v<<endl;
return 0;
}
2.3 命名空间是不连续的,可以多次定义
namespace a
{
int r;
}
namespace a
{
int add()
{}
}
namespace a
{
class haha
{};
}
r,add(),haha都是a中的名称;
int main()
{
a::r=0;
a::add();
a::class haha hh;
}
2.4 命名空间中函数可以只声明,而定义放在命名空间外的全局范围内,定义时加上作用域即可;(和类中定义函数一样)
namespace a
{
int add();
}
int a::add()
{}
int multi()//普通的全局函数
{}
2.5 命名空间定义完之后可以取别名
取别名就很灵活,就和定义变量一样;
namespace a
{
int r;
}
namespace b=a;
int main()
{
namespace c=a;
namespace d=b;
a::r=0;
cout<<a::r<<endl;
b::r=1;
cout<<b::r<<endl;
c::r=2;
cout<<c::r<<endl;
d::r=3;
cout<<d::r<<endl;
return 0;
}
0
1
2
3
2.6 无名命名空间
即定义时没有写名字的命名空间,表示{}中内容只能被当前文件访问;相当于{}中的定义都加上了static;
namespace
{
int b=2;
}
int main()
{
cout<<b<<endl;//输出2,可以把b看作是static全局变量;
}
3、命名空间的使用
3.1 首先介绍::这个符号
两个冒号是作用域运算符,表明归属,右属于左;
如果前面不加作用域名,则默认为全局;
namespace a
{
int r=1;
}
int r=2;
int main()
{
cout<<r<<a::r<<::r<<endl;
return 0;
}
r和::r都是全局的r;
212
3.2 一般情况使用
3.2.1 少量少次使用到
如果一个命名空间内的成员只有一两次使用到,则只需要在使用时往前面加上作用域即可,例如一个程序中只使用了一次两次命名空间a中的int型b,每次使用时加上a::即可;
namespace a
{
int b=1;
}
int main()
{
cout<<a::b<<endl;
}
3.2.2 少量成员被频繁使用到
举例:
using a::b;表示在这句代码之后凡是用到了b,则用的是a中的b;
namespace a
{
int r=1;
int v=2;
}
int r=3;
int v=4;
int main()
{
cout<<r<<v<<endl;
using a::r;
using a::v;
cout<<r<<v<<endl;
return 0;
}
34
12
using a::b的注意点:
上述中全局变量r,v,c和命名空间a中的不会起冲突,结果也和预想一样;那么局部变量呢?结果是不行,如果成员名称有对应的局部变量,那么using语句后的变量,编译器并不能判断选哪个,会报错;
namespace a
{
int r=1;
}
int main()
{
int r=6;
using a::r;//报错
cout<<r<<endl;
//C/C++编译规则:{}的局部内,优先选局部变量,
//而代码要求用a中的变量,编译器不知道该听谁的;
return 0;
}
3.2.3 多个成员被频繁使用到;
当多个甚至全局成员都要被频繁使用时,用using namespace a;来表示这句代码之后出现的成员,如果在其他地方找不到,则去a中找;a中能找到的话就不会报使用未声明变量的错误了;
最为常见的就是std;像cout,cin等都是在std命名空间内的;而我们自己没有定义cout;为了在写程序中使用cout,我们将using namespace std;放在程序开头,则接下来写代码出现的cout等都将使用std中的cout;
using namespace std;
namespace a
{
int r=1;
int v=2;
int c=3;
}
int main()
{
int r=3;
int v=4;
int c=5;
using namespace a;
//虽然声明使用a,
//但是在这个局部范围内能找到r,v,c,而且他们本身作为局部变量会被优先选用;
//直接用而不去a中找;
cout<<r<<v<<c<<endl;
return 0;
}
345
而如果没有找到则去a中找;
using namespace std;
namespace a
{
int r=1;
int v=2;
int c=3;
}
int main()
{
using namespace a;
//声明使用a,
//但是在这个局部范围内找不到r,v,c,
//则去a中找;
cout<<r<<v<<c<<endl;
return 0;
}
123
当然,前提是a中确实有,如果没有肯定报错;
using namespace a的注意点:
那么如果没有局部变量而有全局变量,同时又使用using namespace a;会怎么样?
using namespace std;
namespace a
{
int r=1;
}
int r=6;
int main()
{
using namespace a;
cout<<r<<endl;//报错
//这个r默认为找全局变量,能找到,
//而代码又要求用a中的成员,也能找到,
//编译器懵逼,报错;
return 0;
}
四 extern “C”
用于C和C++混合编程,在C++程序中,要求一些函数以C语言的方式运行,或者说以C的方式来编译;一般用在头文件中;
因为C和C++编译方式的不同,比如编译后函数名的组成不同,导致C++直接用C的库函数时出错,解决方法就是在声明这些库函数的时候加上extern “C”;要求用C语言方式来编译,
1、怎么加
方式一:头文件中加,表示这些函数要以C方式编译;
在C语言的头文件中加,则后面的函数可以被C++调用,而且在g++编译时仍然保持C方式编译;
extern "C" // 后跟函数描述
extern "C"{
// 多个函数描述
}
方式二:C++文件中
表示这些内容以C方式编译;
#ifdef __cplusplus//C++的宏
extern "C" {
#endif /* __cplusplus */
{}//中间的内容即是按C方式编译的内容;
#ifdef __cplusplus
}
#endif /* __cplusplus */
参考
一分钟带你了解C++中new和delete的使用方法
C++中new的使用方法
c++ new的用法(总结)
浅谈c++中的new/delete和new[]/delete[]
C++命名空间namespace的详解
混合使用C和C++