C++基础

命名空间

命名空间定义(namespace)

  1. 普通的命名空间
  2. 命名空间的嵌套
  3. 多个命名空间名称相同,编译器会自动合并
//普通的命名空间
namespace N1  // N1为命名空间的名称
{
	int a = 10;
	int b = 20;

	int Add(int left, int right)
	{
		return left + right;
	}
}
//命名空间可以嵌套
//举个例子:N2代表计算机学院,包含N3物联网工程
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;
	}
}

命名空间的使用

  1. 命名空间 + ::(作用域限定符)
  2. 使用using将命名空间中成员引入
  3. 使用using namespace 命名空间名称引入
// 作用域限定符
int a = 10;

int main()
{
    int a =1;
    printf("%d\n",a);  //访问main函数中的a
    printf("%d\n",::a); //访问全局变量a
    return 0;
}

//命名空间使用
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;
		}
	}
}

N2::N3::a;
using namespace N2;
int main()
{
    //命名空间 + ::
    printf("%d\n",N2::N3::a); //50
    //using N2::N3::a;
    printf("%d\n",a);  //50
    //using namespace N2;
    printf("%d\n",a);  //50
    
}

缺省参数

缺省参数:声明或定义时为函数的参数指定一个默认值,调用时如果没有指定则采用该值

全缺省参数:所有参数都有默认值

半缺省参数:部分函数带有缺省值,从右向左依次给出

//全缺省参数
void TestFunc(int a = 0,int b = 1,int c = 2)
{
	cout << a << " " << b << " " << c << endl;
}
//半缺省参数(如果只给a的话,会报错,只给a和c也会报错)
//必须从右往左依次给出
void Test2Func(int a, int b, int c = 1)
{
	cout << a << " " << b << " " << c << endl;
}

int main()
{
	//全缺省参数
	TestFunc(10, 20, 30);//10 20 30
	TestFunc(); //0 1 2
	TestFunc(10);//10 1 2
	TestFunc(10, 20);//10 20 2
	//半缺省参数
	Test2Func(10, 20, 30); //10 20 30
	Test2Func(10, 20);// 10 20 1
	//Test2Func(10);//报错
	//Test2Func();//报错
	return 0;
}

半缺省参数不能再函数声明和定义同时出现,一般放到声明的位置

缺省值必须是常量或者全局变量

//编译器报错:如果两个位置提供的值不同,编译器无法确定使用哪个缺省值
void TestFunc(int a = 5);

void TestFunc(int a = 10)
{}

int main()
{
	return 0;
}

函数重载

同一作用域声明几个功能类似的同名函数,同名函数的形参列表(个数、类型、次序)必须不同

#include <iostream>
using namespace std;
//使用cout<<""<<ebdl;要引头文件
int Add(int left, int right)
{
	return left + right;
}
//参数类型不同
double Add(double left, double right)
{
	return left + right;
}
//参数顺序不同
double Add(double left, int right)
{
	return left + right;
}
double Add(int left, double right)
{
	return left + right;
}
//个数不同
double Add(double a)
{
	return a;
}

int main()
{
	cout << Add(1, 2) << endl;  //3
	cout << Add(1.1 , 2.0) << endl; //3.1
	cout << Add(1.2, 2) << endl; //3.2
	cout << Add(1, 2.3) << endl;//3.3
	cout << Add(5.2) << endl;  //5.2

	return 0;
}

与返回值类型无关

//编译器报错
int Add(int left, int right)
{
	return left + right;
}

double Add(int left, int right)
{
	return left + right;
}

无参函数与同名全缺省参数不要同时存在

void TestFunc()
{}
void TestFunc(int a = 0)
{}

int main()
{
    //编译器报错:对重载函数的调用不明确
	TestFunc();
	return 0;
}
  • C语言和C++对函数的名字的修饰规则

C语言中,编译器对函数名字的修饰规则:仅在函数名字前加 _

C++函数名字修饰规则:将参数的类型增加到最终的名字中,如 double Add(double left, double right);在编译器中的修饰 (?Add@@YANNN@Z)

两个函数仅仅只在返回值类型上不一样,不能形成重载

  • extern"C"

有时候在C++工程中将代码按照C的风格来编译,在函数前加extern"C"

引用

引用类型必须和引用实体必须是同类型

int main()
{
	int a = 10;
	int& ra = a;
    //double& rd = a;//错误
	ra = 20;
	cout << a << endl;//20
	cout << &a << endl;
	cout << &ra << endl;
	return 0;
}

特性

  1. 一个变量可取多个引用
  2. 引用在定义时必须初始化
  3. 一旦引用一个实体,再不能引用其他实体
int main()
{
	//引用的生命周期没有实体的长
	int a = 10;
	int b = 5;
	int& ra = a;
	//int&rb;   没有初始化,错误
	//int& ra = b;  多次引用,错误
	int& rra = a;
	ra = 20;
	cout << &rra << endl;
	cout << &a << endl;
	cout << &ra << endl;
	return 0;
}

常引用

int main()
{
	//方式一
	const int a = 10;
	//int& ra = a;  error
	const int & ra = a;
	//ra = 20;  error
	//方式二
	const int& b = 10;
	//方式三
	double d = 12.34;
	//int& rd = d;  类型不匹配
	const int& rd = d; 
	//const 类型的引用,可以给一些临时变量取别名
	d = 10.34;

	cout << rd << " " << &rd << endl;
	cout << d << " " << &d << endl;
    //rd 和 d不是同一块空间
	return 0;
}

引用的使用场景

  • 引用作为函数的参数

实参--->值传递 ,可以通过修改形参改变外部实参

如果是引用类型的参数,但在函数体中不需要改变形参,最好传递const 类型的引用

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
//注意:如果是引用类型的参数,但在函数体中不需要改变形参,最好传递const 类型的引用
void TestRef(const int& a)
{}

int main()
{
	int a = 10;
	int b = 20;
	Swap(a, b);
	cout << "a=" << a << endl;//20
	cout << "b=" << b << endl;//10

	return 0;
}
  • 引用作为函数的返回值类型

如果以引用的方式作为函数的返回值类型,不能返回栈上的空间

如果以引用的类型返回,那么返回的变量的生命周期一定要比函数的生命周期长

int & TestRet()
{
	int ret = 10;
	//ret 是栈上的空间,而栈空间随着函数的结束已经被系统收回了
	return ret;
}

int main()
{
	int& a = TestRet(); //a所引用的空间为一段无效的空间
	printf("%d\n", a);
	printf("%d\n", a);
	printf("%d\n", a);

	return 0;
}

引用和指针的区别

  • 传值、传指针、传引用的效率比较
/*
这是一段测试传值、传指针、传引用的效率测试代码
*/
#include <Windows.h>

struct A
{
	int array[10000];
	int size;
};
/*/
void TestRef(A param)
{

}
void TestRef(A* param)
{

}
void TestRef(A& param)
{

}
*/
void Test()
{
	A a;
	size_t begin = GetTickCount();  
	for (size_t i = 0; i < 10000000; ++i)
	{
		//TestRef(a);  传值
		//TestRef(&a); 传地址
		//TestRef(a);  传引用
	}
	size_t end = GetTickCount();
	cout << end - begin << endl;
}

int main()
{
	Test();
	return 0;
}

传指针和传引用效率差不多

  • 指针和引用的区别
int main()
{
	int a = 10;
	int *pa = &a;
	*pa = 20;

	int& ra = a;
	ra = 20;
	return 0;
}

相同点

引用和指针在底层的处理方式完全相同,引用就是按照指针的方式处理

不同点

  1. 引用在定义时必须初始化,指针没有要求
  2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  3. 没有NULL引用,但有NULL指针
  4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  6. 有多级指针,但是没有多级引用
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全

引用的类型

int a = 10;
int& ra = a;  //int& ---> int* const
const int& cra = a; //const int* const

内联函数(inline)

概念

inline int Add(int left, int right)
{
	return left + right;
}

int main()
{
	Add(1, 2);
	return 0;
}

内联函数:在编译阶段已经展开(以空间换时间)

如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联

如果想看编译器如何处理内联函数的,将编译器的Debug更改为Release模式

宏和内联函数

  • 宏的优缺点

优点:增强代码的复用性,提高性能

缺点:不方便调试,代码的可读性差,容易误用,不安全(没有类型安全的检查)

  • C++中替换宏

宏函数---->inline

宏常量---->const 修饰(常量&具有宏的属性(替换))

auto关键字(C++11)

auto

在C++11以前:auto修饰的变量,是具有自动存储器的局部变量

C++11:auto是类型声明时的一个占位符,编译器在编译期会将auto替换为变量实际的类型。

int main()
{
	int a = 10;
	auto b = 20;
	auto c = 'c';
	auto d = 12.34;
	//auto e; 错误,使用必须初始化
	//typeid(b).name() 查看类型
	cout << typeid(b).name() << endl;  //int
	cout << typeid(c).name() << endl;  //char
	cout << typeid(d).name() << endl;  //double
 	return 0;
}

使用细则

int main()
{
	int a = 10;
	auto pa1 = &a;
	auto* pa2 = &a;
	auto& ra = a;
	ra = 20;
	//	auto c = 0,d =1.0; //错误,同行出现多个类型
	cout << typeid(pa1).name() << endl;  //int*
	cout << typeid(pa2).name() << endl;  //int*
	cout << typeid(ra).name() << endl;  //int
	cout << a << endl; //20  说明ra修改了a的值
	return 0;
}
//编译器报错:编译器无法对a的实际类型进行推倒
void TestFunc(auto a)
{}

int main()
{
	int a[] = { 1,2,3 };
	//编译器报错
	auto b[3] = a;
	return 0;
}
  1. auto在定义变量时必须初始化
  2. auto 和 auto* 没有区别,但是引用的时候必须加上& auto &
  3. 同行auto定义多个变量时,必须为同类型变量
  4. auto不能作为函数参数,不能直接用来声明数组
  5. auto不能定义类的非静态成员变量
  6. 实例化参数时,不能使用auto作为模板参数

优势

在拥有初始化表达式的复杂类型变量声明时的简化

namespace N1
{
	int a;
	namespace N2
	{
		typedef int DataType;
		int b;
	}
}

int main()
{
	N1::N2::DataType a = N1::N2::b;
	//使用auto
	auto a2 = N1::N2::b;
	return 0;
}

可以免除程序员在一些类型声明时的麻烦,或者避免一些在类型声明时的错误

int main()
{
	const float pi = 3.14;
	double r = 2.0;
    //c的类型让编译器来推倒
	auto c = 2 * pi * r;

	cout << c << endl;  //12.56
	cout << typeid(c).name() << endl;  //double

	return 0;
}

范围for

范围for:for(auto & e:array){ }要求
用“:”隔开for循环范围确定
第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围迭代对象要实现++/--操作
int main()
{
	int array[] = { 1,2,3,4,5,6,7,8,9,0 };
	//之前的方法
	//for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
	//{
	//	array[i] *= 2;
	//}
	//for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
	//{
	//	cout << array[i] << " ";
	//}
	//cout << endl;

	//范围for
	for (auto& e : array)
	{
		e *= 2;
	}
	for (auto& e : array)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

指针空值--->nullptr(C++11)

在C语言中NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量容易发生混淆,Test(0);Test(NULL);没有区别

为了避免混淆,C++11提供了nullptr,即:nullptr代表一个指针空值常量

nullptr是有类型的,其类型为nullptr_t,仅仅可以被隐式转化为指针类型

void TestFunc(int)
{
	cout << "int" << endl;
}
void TestFunc(int* pa)
{
	cout << "int*" << endl;
}
int main()
{
	TestFunc(0);  //int 
	TestFunc(NULL); //int
	TestFunc(nullptr); //int*
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值