1.C++兼容C

//.c
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
//.cpp
#include<iostream>
using namespace std;
int main()
{
cout << "hello world\n" << endl;
return 0;
}

2.命名空间

在C/C++的学习中,变量,函数名以及类这些东西,定义在全局定义域中,会导致很多重突,

使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名
冲突或名字污染,

//命名空间就是将这些东西进行本地化,防止冲突。

namespace命名空间域的关键字

#include <stdlib.h>
int rand = 10;
int main()
{
// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”
printf("%d\n", rand);
return 0;
}
namespace的定义

1.namespace后面跟命名空间名,{命名空间的成员变量},{}里面是变量,函数,类型。

2.namespace的本质是定义出一个域,这个域于全局域互相独立,所以就解决了变量冲突

3.C++中有全局域,局部域,命名空间域,类域。域影响的是编译时,语法查找一个变量/函数/类型出处的逻辑,有了域的隔离,命名冲突问题也就解决了。

局部域和全局域不仅影响语法查找逻辑,还影响变量的生命周期。命名空间域和类域不影响变量的生命周期。

4.namespace只可以在全局定义,也可以嵌套定义。

5.项目中多文件中有多个namespace,会被认为是一个,不会冲突。

6.C++标准库都放在一个std(standard)的命名空间中。

//#include<stdio.h>
//#include<stdlib.h>
//namespace只能定义在全局内,可以嵌套定义
namespace jiege
{
	int rand = 10;
	int Add(int left, int right)
	{
		return left + right;
	}
	struct Node
	{
		struct Node* next;
		int val;
	};
	//zhangsan和lisi属于不同的域,但可以定义相同的变量
	namespace zhangsan
	{
		int rand = 1;
		int Add(int left, int right)
		{
			return left + right;
		}
	}
	namespace lisi
	{
		int rand = 2;
		int Add(int left, int right)
		{
			return (left + right) * 10;
		}
	}
}
#include<iostream>

using namespace std;
int main()
{
	struct jiege::Node* node = { 0 };
	scanf("%d ",node->val );
	cin>>node->val;
	cout << jiege::Add(1, 5) << endl;
	
	printf("%d \n", jiege::zhangsan::rand);
	printf("%d \n", jiege::lisi::rand);
	return 0;
}
//多文件命名相同名字的namespace会认为是一个namespace,不会冲突
命名空间的使用

1.编译查找的时候只会在全局域和局部域,不会到命名空间里面去找。

想要使用命名空间里面的成员变量,有以下三种方式

a.指定命名空间进行访问

b.展开命名空间的部分成员

c.展开命名空间内的全体成员


//#include<iostream>
//namespace jiege
//{
//	int a;
//	int b;
//}
////项目中经常访问的不存在冲突的变量
//using jiege::b;
////展开命名空间中全部的成员
using namespace jiege
//int main()
//{
//	printf("%d\n", b);
//	//指定命名空间进行访问
//	//printf("%d\n", jiege::a);
//	return 0;
//}

3 C++的输入与输出

1.<iostream>是标准的输入输出流库,定义了标准的输入输出对象。

2.std::cin istream类的对象他主要面向窄字符的标准输入流。

3std::cout ostream类的对象,面向窄字符的标准输出流。

4.std::endl 是一个函数,流插入输入时,相当于换行字符➕一个刷新缓冲区

5.<<是流插⼊运算符,>>是流提取运算符。

6.使用C++的输入输出会更加方便,不需要C语言中printf scanf 进行手动指定格式,C++的输入输出自动识别变量类型(本质是通过函数重载)C++会更好的支持自定义类型的输入输出。

7.IO流涉及类和对象,运算符重载、继承等很多⾯向对象的知识
8.cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要
通过命名空间的使⽤⽅式去⽤他们。
9.⽇常练习中我们可以using namespace std,实际项⽬开发中不建议using namespace std。
//输入或输出
//#include<iostream>
//using namespace std;
//int main()
//{
//	int a = 10;
//	double b = 0.11111;
//	char c = 'x';
//	cin >> a >> b >> c;
//	cout << a <<' ' << b <<'\n'<< c << endl;
//
//	return 0;
//}
//#include<iostream>
//using namespace std;
//int main()
//{
//	// 在io需求⽐较⾼的地⽅,如部分⼤量输⼊的竞赛题中,加上以下3⾏代码
//	// 可以提⾼C++IO效率
//	ios_base::sync_with_stdio(false);
//	cin.tie(nullptr);
//	cout.tie(nullptr);
//	return 0;
//}

4.缺省函数

缺省函数是在定义或声明时为函数参数指定一个缺省值,在调用该函数时,如果没有指定实参,则采用该形参的缺省值。否则使用指定的实参,缺省参数有全缺省和半缺省(缺省参数也可以叫默认参数)

全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定必须从左到右依次给缺省值,不能跳跃型给缺省值。

带缺省函数的调用,必须从左到右依次给缺省值,不能跳跃的给缺省值。

函数的定义与声明分开时,缺省参数不能同时在定义和声明里面出现,C++规定函数声明给缺省值

//缺省参数
#include<iostream>
using namespace std;
//void Func(int a = 10)
//{
//	cout << a << endl;
//}
//int main()
//{
//	Func();
//	Func(0);
//	return 0;
//}
// 全缺省
//void Func1(int a = 10, int b = 20, int c = 30)
//{
//	cout << "a = " << a << endl;
//	cout << "b = " << b << endl;
//	cout << "c = " << c << endl << endl;
//}
//// 半缺省
//void Func2(int a, int b = 10, int c = 20)
//{
//	cout << "a = " << a << endl;
//	cout << "b = " << b << endl;
//	cout << "c = " << c << endl << endl;
//}
//int main()
//{
//	Func1();
//	Func1(1);
//	Func1(1, 2);
//	Func1(1, 2, 3);
//	Func2(100);
//	Func2(100, 200);
//	Func2(100, 200, 300);
//	return 0;
//}
void STInit(ST* ps, int n = 4);
// Stack.cpp
#include"Stack.h"
// 缺省参数不能声明和定义同时给
void STInit(ST* ps, int n)
{
assert(ps && n > 0);
ps->a = (STDataType*)malloc(n * sizeof(STDataType));
ps->top = 0;
ps->capacity = n;
}
// test.cpp
#include"Stack.h"
int main()
{
ST s1;
STInit(&s1);
// 确定知道要插⼊1000个数据,初始化时⼀把开好,避免扩容
ST s2;
STInit(&s2, 1000);
return 0;
}

5.函数重载

C语言不支持在同一作用域出现同名函数
C++支持在同一作用域出现同名函数,但要求这些同名函数其形参不同,参数个数和参数类型不同。c++函数表现出来多态行为,使用更加灵活。
#include<iostream>
using namespace std;
// 1、参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
// 2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
// 3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
// 返回值不同不能作为重载条件,因为调⽤时也⽆法区分
//void fxx()
//{}
//
//int fxx()
//{
// return 0;
//}
// 下⾯两个函数构成重载
// f()但是调⽤时,会报错,存在歧义,编译器不知道调⽤谁
void f1()
{
cout << "f()" << endl;
}
void f1(int a = 10)
{
cout << "f(int a)" << endl;
}
int main()
{
Add(10, 20);
Add(10.1, 20.2);
f();
f(10);
f(10, 'a');
f('a', 10);
return 0;
}

6.引用

1引用的定义

引用就是给变量取别名,它与变量共用同一块空间。

类型& 引⽤别名 = 引⽤对象;
int main()
{
int a = 0;
// 引⽤:b和c是a的别名
int& b = a;
int& c = a;
// 也可以给别名b取别名,d相当于还是a的别名
int& d = b;
++d;
// 这⾥取地址我们看到是⼀样的
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}

2引用的特性

引用在定义时必须初始化

一个变量可以有多个引用

一旦引用了一个实体,就不能引用其他实体

//int main()
//{
//	int a = 0;
//	//int& ra;
//	int& b = a;//创建以后必须初始化
//	int c = 20;
//	//int&c = a;
//	b = c;//这是一个赋值
//	//一个变量可以有多个引用
//	//引用一旦有了实体,就不能引用其他实体
//	cout << &a << endl;
//	cout << &b << endl;
//	cout << &c << endl;
//	return 0;
//}
//void Swap(int& rx, int& ry)
//{
//	int tmp = rx;
//	rx = ry;
//	ry = tmp;
//}
//int main()
//{
//	int x = 0, y = 1;
//	cout << x << " " << y << endl;
//	Swap(x, y);
//	cout << x << " " << y << endl;
//	return 0;
//}
//typedef int STDataType;
//typedef struct Stack
//{
//	STDataType* a;
//	int top;
//	int capacity;
//}ST;
//void STInit(ST& rs, int n = 4)
//{
//	rs.a = (STDataType*)malloc(n * sizeof(STDataType));
//	rs.top = 0;
//	rs.capacity = n;
//}

3引用的使用

引用传参和引用做返回值,减少拷贝,提升效率,改变引用对象的同时改变被引用对象

引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。
引⽤返回值的场景
引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引⽤跟其他
语⾔的引⽤(如Java)是有很⼤的区别的,除了⽤法,最⼤的点,C++引⽤定义后不能改变指向,
Java的引⽤可以改变指向。
//引用做返回值
//int& STTop(ST& rs)
//{
//	//assert(rs.top > 0);
//	return rs.a[rs.top];
//}
//// 栈顶
//void STPush(ST& rs, STDataType x)
//{
//	//assert(ps);
//	// 满了, 扩容
//	if (rs.top == rs.capacity)
//	{
//		printf("扩容\n");
//		int newcapacity = rs.capacity == 0 ? 4 : rs.capacity * 2;
//		STDataType* tmp = (STDataType*)realloc(rs.a, newcapacity *
//			sizeof(STDataType));
//		if (tmp == NULL)
//		{
//			perror("realloc fail");
//			return;
//		}
//		rs.a = tmp;
//		rs.capacity = newcapacity;
//	}
//	rs.a[rs.top] = x;
//	rs.top++;
//}
//int main()
//{
//	ST st;
//	STInit(st);
//	STTop(st) = 3;//直接改变栈顶元素的值
//	cout << STTop(st) << endl;
//	return 0;
//}

3Const引用

可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访
问权限在引⽤过程中可以缩⼩,但是不能放⼤。
临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,
C++中把这个未命名对象叫做临时对象
C++规定临时对象具有常性
//int main()
//{
//	const int a = 10;//此时a具有常属性只可以读
//	//int& ra = a;
//	const int& ra = a;//引用所产生的别名权限必须与原来小于等于
//	//临时对象具有常性
//	//表达式的结果
//	//常量
//	//隐式类型转换
//	//引用以后临时变量的生命周期随着引用走
//	const int& rb = 30;
//	int b = 30;
//	const int& rb = b;
//	const int&rd=(a + b);
//	int rd = (a + b);//这是一种赋值拷贝
//
//
//	//这里是权限放大
//	const int a = 10;//此时a具有常属性只可以读
//	//int& ra = a;
//	//单纯的拷贝给另一个,不存在权限放大
//	const int a = 10;
//	int rd = a;
//	//int& rd=(a+b);
//
//	double d = 12.34;
//	int i = d;
//	const int& ri = d;
//
//
//	return 0;
//}

4指针和引用的关系

语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。
引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。
sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下
占4个字节,64位下是8byte)
指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。

7.inline

inline修饰的函数叫内联函数,C++在编译的时候会在调用的地方展开内联函数,这样就不需要额外的建立函数栈帧,就可以提高效率

inline适⽤于频繁
调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。
C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调
试,C++设计了inline⽬的就是替代C的宏函数。
inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地
址,链接时会出现报错。
//inline 不建议声明和定义在两个文件中,导致链接错误
//inline 函数直接定义在.h文件中
//inline void Swap(int& x, int& y)
//{
//
//}
//int main()
//{
//	int a = 10, b = 0;
//	Swap(a, b);
//
//	return 0;
//}
//int ADD(int& a, int& b)
//{
//	return a + b;
//}
//// 为什么不能加分号?
//// 为什么要加外⾯的括号?
//// 为什么要加⾥⾯的括号?
//#define ADD(a,b) ((a)+(b))
//int main()
//{
//	cout << ADD(1, 2) << endl;
//	cout << ADD(1, 2)*5 << endl;
//	int x = 1, y = 2;
//	ADD(x & y, x | y); //如果没有括号的话等价于 -> (x&y+x|y)
//	return 0;
//}

8.nullptr

//nullptr
//NULL在C++文件中定义为字面常量0
//NULL在c文件中被定义为(void*)0
//C++11中引⼊nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换
//成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被
//隐式地转换为指针类型,⽽不能被转换为整数类型。

//void f(int x)
//{
//	cout << "f(int x)" << endl;
//}
//void f(int* ptr)
//{
//	cout << "f(int* ptr)" << endl;
//}
//int main()
//{
//	//f(0);
//	//f((void*)0);
//	int* p1 = nullptr;
//	//int p2 = nullptr;
//
//	return 0;
//}

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐