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

C++学习笔记——命名空间&缺省参数&函数重载&引用

命名空间:

用于解决名字冲突,相当于一个作用域

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.作返回值
如果用引用作为函数的返回值类型不能返回函数栈上的空间
C++学习笔记——命名空间&缺省参数&函数重载&引用
如果一定要用引用作为返回值,返回的变量生命周期一定要比函数的生命周期长
比如可以这样稍作修改:
C++学习笔记——命名空间&缺省参数&函数重载&引用

传值、传地址、传引用效率比较

#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;
}

通过上面代码的比较,我们发现引用和指针在传参上的效率几乎相同

引用与指针的区别
C++学习笔记——命名空间&缺省参数&函数重载&引用
不同点
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;
}