C++学习笔记——命名空间&缺省参数&函数重载&引用
C++学习笔记——命名空间&缺省参数&函数重载&引用
我的印象笔记原链接:https://app.yinxiang.com/shard/s49/nl/21702903/a003b01c-8d55-4066-982f-6a856b688fc1
C++:
1.解决C语言中设计不好或者使用不是很方便的语法—>优化
2.增加新的语法特性
注:extern “C”:在C++工程中,将代码按照C语言的风格来编译
C++关键字 (C++98)----63
命名空间:
用于解决名字冲突,相当于一个作用域
namespace N1
{
1.变量
2.函数
3.命名空间(嵌套)
}
命名空间的定义方式:
1.普通命名空间(只包含变量和函数)
2.命名空间可以嵌套
3.可以创建多个相同名字的命名空间–>合并
命名空间的访问方式:
1.在成员前+ N:: (N为命名空间的名字,::为作用域限定符—默认访问全局的)
2.使用using来指定访问变量:using N2::N3::a;
3.使用using来指定访问命名空间:using namespace N2;
标准输入/输出
使用标准输入cin和标准输出cout时,必须包含**头文件以及std标准命名空间**
//为了和c区分,c++98之后头文件不需要包含.h
// using namespace std;标准命名空间
cin标准输入(键盘):int a = 0;
double b = 12.34;
cin>>a>>b;
cout标准输出(控制台):cout<<10<<" "<<12.34<<endl; //好处是不需要加数据格式控制(%d,%c之类)
(dec:十进制
oct:八进制
hex:十六进制
二进制可以使用bitset<> 把要输出的数变成二进制存储输出)
“备胎”–>缺省参数
概念:声明或定义函数时为函数的参数指定一个默认值(调用时如果没有指定实参就会使用默认值)
全缺省参数:所有参数都有默认值
对于全缺省参数,如果调用函数时只传递了一部分实参,则实参从左往右传递,其余采用默认值
半缺省参数:部分参数带有缺省值,必须从右向左依次给出
对于半缺省参数,要注意对没有给出缺省值的参数传递实参,实参同样从左往右传递
注意:
1.半缺省参数必须从右往左依次给出,不能间隔着给
2.缺省参数不能在函数声明和定义中同时出现(为了避免出现声明和定义不一致情况),最好在声明的位置
3.缺省值必须是常量或者全局变量
4.C语言不支持(编译器不支持)
“一词多义”–>函数重载:
概念:是函数的一种特视情况,C++允许在同一作用域中声明几个功能类似的同名函数,但这些同名函数的形参列表(参数个数、类型、顺序)必须不同,常用来处理功能类似数据类型不同的问题 //与返回值类型无关,如果只是返回值类型不同,则不能构成重载
二义性:无参函数和同名的全缺省函数不能同时存在
C语言中不支持函数重载是因为:
C语言中编译器对函数名字的修饰规则:只是简单地在函数名字前添加下划线
C++中支持函数重载是因为:
//在vs中通过只给声明不给定义的方式调用函数,编译成功,链接时报错就可以看到编译器对函数名字的修饰规则↓↓↓
C++中编译器对函数名字的修饰规则(_cdecl:C语言缺省调用约定):
int ADD(int left,int right); —> [email protected]@[email protected] ?函数名@@YA参数表@Z
参数表(返回值和形参类型)符号表示:
void - X
int - H
unsigned int - I
float - M
double - N
bool - N
char - D
short - F
long - J
unsigned long - K
“外号”–>引用
概念:给已存在的变量取一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
类型& 引用变量名(对象名)= 引用实体
引用特性:
1.引用在定义时必须初始化
2.一个变量可以有多个引用
3.引用一旦引用了一个实体,就不能再引用其他实体
注意:
1.引用类型必须与引用实体是同类型的
2.引用常量实体时要加const修饰
3.一般情况下,因为引用与实体共用同一块内存空间,所以改变引用的值也就是改变了实体的值
4.引用类型与引用实体不同时加const可以通过编译,此时编译器会为引用创建一个临时变量,这个临时变量具有常属性
使用场景:
1.作形参
如果不需要通过形参修改实参的值,最好的方法是在形参引用前加上const修饰
2.作返回值
如果用引用作为函数的返回值类型,不能返回函数栈上的空间
如果一定要用引用作为返回值,返回的变量生命周期一定要比函数的生命周期长
比如可以这样稍作修改:
传值、传地址、传引用效率比较:
#include <Windows.h>
struct A
{
int array[10000];
};
void TestFunc(A& a)
{}
void TestRefPtr()
{
A a;
size_t start = GetTickCount();
for (size_t i = 0; i < 1000000; i++)
TestFunc(a);
size_t end = GetTickCount();
cout << end - start << endl;
}
int main()
{
TestRefPtr();
system("pause");
return 0;
}
通过上面代码的比较,我们发现引用和指针在传参上的效率几乎相同
引用与指针的区别:
不同点:
1.引用在定义时必须初始化,指针没有要求(但最好有一个合法的指向)
2.引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任意一个同类型实体
3.没有NULL引用,但有NULL指针
4.在sizeof中含义不同:引用的结果为引用类型的大小,但指针始终是地址空间所占的字节个数(32位平台为4个字节)
5.引用自加是引用的实体加1,指针自加是指针向后偏移一个类型的大小
6.有多级指针,但没有多级引用(拓展:C++11中将const int&& rra = 10;这种形式称为右值引用,将普通引用称为左值引用)
7.访问实体的方式不同,指针需要显示解引用,而引用由编译器自己处理
总结来说也可以得出;
1.引用更安全。因为指针在使用之前必须要判空,而引用不需要(因为规定了引用在定义时必须初始化)
2.引用更简洁。引用在使用时代码比较清晰,也不需要解引用操作,写起来简单,看起来舒服,还可以达到指针的效果
最后附上我的学习代码,仅供参考
#include <iostream>
using namespace std;
#if 0
//命名空间
#include <stdio.h>
#include <stdlib.h>
//普通命名空间
namespace N1
{
int a = 10;
int b = 20;
int Add(int left,int right)
{
return left + right;
}
}
//命名空间可以嵌套
namespace N2
{
int c = 30;
int d = 40;
int Sub(int left, int right)
{
return left - right;
}
namespace N3
{
int a = 50;
int b = 60;
int Mul(int left, int right)
{
return left * right;
}
}
}
//可以创建多个相同名字的命名空间
namespace N1
{
int Div(int left, int right)
{
return left / right;
}
}
//using N2::N3::a;
using namespace N2;
int main()
{
/*printf("%d\n", ::a);
printf("%d\n", N1::a);
printf("%d\n", N2::N3::a);*/
//printf("%d\n", a);
printf("%d\n", Sub(d, c));
system("pause");
return 0;
}
#endif
#if 0
//标准输入/输出
int main()
{
int a = 0;
double b = 12.34;
cin >> a >> b;
cout << a <<" "<< b << endl;
//cout << hex << a<<endl;
cout << 10 << " " << 12.34 << endl;
cout << "hello world!" << endl;
system("pause");
return 0;
}
#endif
#if 0
//缺省参数
int g_a = 9;
void TestFunc(int a = g_a)
{
cout << a << endl;
}
//全缺省参数:所有参数都有默认值
void TestFunc1(int a = 0, int b = 1,int c=2)
{
cout << a << " " << b << " " << c << endl;
}
//半缺省参数:部分参数带有缺省值,必须从右向左依次给出
void TestFunc2(int a, int b = 1, int c = 0)
{
cout << a << " " << b << " " << c << endl;
}
int main()
{
/*TestFunc();
TestFunc(10);*/
/*TestFunc1();
TestFunc1(10, 20, 30);
TestFunc1(10);
TestFunc1(10, 20);*/
TestFunc2(10);
TestFunc2(10,20);
TestFunc2(10,20,30);
system("pause");
return 0;
}
#endif
#if 0
//函数重载
int ADD(int left, int right)
{
return left + right;
}
double ADD(double left, double right)
{
return left + right;
}
char ADD(char left, char right)
{
return left + right;
}
//形参列表不同(个数、类型、顺序)
void Test()
{}
void Test(int a)
{}
void Test(double a)
{}
void Test(int a, double b)
{}
void Test(double a, int b)
{}
int main()
{
//cout << ADD(1, 2) << endl;
//cout << ADD(1.1, 2.2) << endl;
//cout << ADD('1', '2') << endl;//ASCLL码相加
system("pause");
return 0;
}
#endif
#if 0
//引用
void Swap(int& left,int& right)
{
int tmp = left;
left = right;
right = tmp;
}
//如果不需要通过形参修改实参的值,最好的方法是在形参引用前加上const修饰
int TestFunc(const int& a)
{
return a;
}
//如果用引用作为函数的返回值类型,不能返回函数栈上的空间
//如果一定要用引用作为返回值,返回的变量生命周期一定要比函数的生命周期长
int& Test()
{
int x = 1;
return x;
}
int main()
{
int a = 10;
int b = 20;
const int c = 30;
int& ra = a;//引用在定义时必须初始化
int& rra = a;//一个变量可以有多个引用
//int& ra = b; //引用一旦引用了一个实体,就不能再引用其他实体
cout << &a << endl;//共用同一块内存空间,所以地址都相同
cout << &ra << endl;
cout << &rra << endl;
const int& rc = c;//引用常量实体必须加const修饰
const int& rd = 10;
double e = 12.34;
const int& re = e;//引用类型与引用实体不同时加const可以通过编译,此时编译器会为引用创建一个临时变量,这个临时变量具有常属性
e = 100;
ra = 20;//一般情况下,因为引用与实体共用同一块内存空间,所以改变引用的值也就是改变了实体的值
rra = 30;
a = 10;
b = 20;
Swap(a, b);
cout << TestFunc(a) << endl;
int& rx = Test();
cout << rx << endl;//10
cout << rx << endl;//随机值 因为第一次输出时Test函数中x所指向的栈上空间已经被cout压栈覆盖了
system("pause");
return 0;
}
#endif
//传值、传地址、传引用效率比较:
#include <Windows.h>
struct A
{
int array[10000];
};
void TestFunc(A& a)
{}
void TestRefPtr()
{
A a;
size_t start = GetTickCount();
for (size_t i = 0; i < 1000000; i++)
TestFunc(a);
size_t end = GetTickCount();
cout << end - start << endl;
}
int main()
{
int a = 10;
int* pa = &a;
*pa = 20;
int &ra = a;
ra = 20;
TestRefPtr();
ra++;
pa++;
char c = 'a';
char& rc = c;
char* pc = &c;
cout << sizeof(rc) << endl;
cout << sizeof(pc) << endl;
system("pause");
return 0;
}