目录
1、命名空间
namespace TT1
{
// 命名空间中的内容,既可以定义变量,也可以定义函数
int a;
int Add(int X, int Y)
{
return X * Y;
}
}
//命名空间可以嵌套
namespace TT2
{
int a;
int b;
int Add(int X, int Y)
{
return X * Y;
}
namespace TT3
{
int c;
int d;
int Sub(int X, int Y)
{
return X - Y;
}
}
}
//同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
namespace TT1
{
int Mul(int X, int Y)
{
return X * Y;
}
}
命名空间就是开辟了一个新的作用域
1.1 命名空间的使用
命名空间的使用有三种
namespace CC
{
int a = 10;
int b = 20;
int Sum(int x, int y)
{
return x + y;
}
}
int main()
{
printf("%d",CC::a);
return 0;
}
namespace CC
{
int a = 10;
int b = 20;
int Sum(int x, int y)
{
return x + y;
}
}
using namespace CC;//把CC命名空间暴露出来
int main()
{
printf("%d",a);
Sum(2,3);
return 0;
}
namespace CC
{
int a = 10;
int b = 20;
int Sum(int x, int y)
{
return x + y;
}
}
using CC::a;//只把a暴露出来
int main()
{
printf("%d",a);
return 0;
}
1.2 CPP优势简单介绍
我们在使用C输出输出时,例如使用printf打印整型变量,浮点型变量等,需要指定类型%d等进行输出,输入定义变量时也需要对类型进行定义,但是在C语言中我们就可省去这些麻烦的操作,CPP中会自动识别类型,下面简单的带大家来看一下。
#include<iostream>//CPP的头文件,和C中stdio.h作用类似
using namespace std;//把C++标准库里面的内容暴露出来
int main()
{
cout<<"Hello world!!!"<<endl;
printf("Hello world!!!\n");//作用一样
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int a;
double b;
char c;
// 可以自动识别变量的类型
cin>>a;
cin>>b>>c;
cout<<a<<endl;
cout<<b<<" "<<c<<endl;
return 0;
}
1.3 auto关键字
int main()
{
int a = 77;
auto b = a;
auto c = 'a';
auto d = 10.1;
//auto d; 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
}
这里的b就是int类型的变量,大家可以使用typeid(b).name()打印出来看一看
int main()
{
int x = 10;
auto a = &x;
auto* b = &x;//auto和auto*没有区别
auto& c = x;//取别名时有区别要加&
auto d = 2, e = 3;//可同时定义多个变量
auto f = 4, g = 4.1;//错误,因为f,g初始化表达式类型不同
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
return 0;
}
auto不能作为函数参数时使用,编译器无法推导a的实际类型
int Test(auto a)//错误
{}
auto也不能直接用来声明数组
void Test()
{
auto b[] = {7, 8, 9};//错误
}
1.5 for循环简化
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)
e *= 2;
for(auto e : array)
cout << e << " ";
return 0;
}
void Test(int arr[])
{
for(auto& e : arr)
cout<< e <<endl;
}
2、缺省参数
void Wwe(int a = 3)
{
count<<a<<endl;
}
int main()
{
Wwe();
Wwe(1);
}
2.1 全缺省
void TestFunc(int a = 10, int b = 20, int c = 30) {
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
2.2 半缺省
void TestFunc(int a, int b = 10, int c = 20) {
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
那么需要注意的问题来了
void TestFunc(int a =10, int b = 10, int c) {
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
像上面这样写行不行,答案是不行的,半缺省参数必须从右往左依次给出,不能间隔给。
那么我们在使用函数时,如果不小心在头文件和源文件中都进行了传参,那么变量的值将会是谁的?
void TestFunc(int a, int b = 10, int c = 20) {
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
//test.h
void TestFunc(int a = 10);
// test.c
void TestFunc(int a = 20)
{}
答案是编译器会报错,因为声明和定义位置同时出现时,两个位置提供的值还不同,编译器就无法确定用哪个缺省值。
3、函数重载
int multi(int x, int y)
{
return x + y;
}
int multi(double x, int y)
{
return x + y;
}
int multi(int x, double y)
{
return x + y;
}
那么为什么C++支持函数重载,C不支持
我们知道C/C++中,程序的运行,需要:预处理、编译、汇编、链接这一个过程
我们以Test.h Test.c main.c三个文件为例进行讲解
现在我们知道了去符号表中去找函数的地址,其中函数重载的关键就在其中
在符号表中C++有函数名修饰规则,而C是没有的,当C中的函数名相同,就会发生冲突,不知道找谁,而在C++中我们在下面举个例子简单解释一下,在符号表中
int multi(double x, int y)
{
return x + y;
}
int multi(int x, double y)
{
return x + y;
}
-z5multidi -z5multiid 数字是函数名字长度,后面两个是函数参数类型,而在C中没有这些东西
4、引用
我们可以先理解为给变量起个外号。你可能以为和typedef一样,但是大大滴不同
void Test()
{
int a = 10;
int& aa = a;//起别名
printf("%p\n", &a);
printf("%p\n", &aa);
}
引用时注意保持类型一致
![](https://i-blog.csdnimg.cn/blog_migrate/4de273951a3567f3d1605e111d2b4583.png)
4.1 使用注意点权限问题
首先我们要先记住权限只能缩小平移,不可放大,权限问题只针对指针,引用
int main()
{
int a=10;
int&b=a;
cout<<typeid(a).name()<<endl;//typeid()打印类型
const int c =20;
//权限放大报错(权限不可放大)因为d可以修改,而c是不能修改的
int& d =c;
//权限平移
const int& d=c;
int e=30;
//权限缩小
const int& f=e;
int i =1;
double dd =i;
double& ddd=i;//错误
const double& ddd = i;//正确
const int&x = 10;
return 0;
}
上面的double dd = i;我们对dd修改并不会对i产生影响,因为这里的i赋值,是创建了一个临时变量,并且这个临时变量是具有常属性的const,我们使用double& dd=i;就相当于放大了权限,就会发生错误,而const double& ddd=i;就是正确的
4.2 做参数、做返回值
做参数
//使用引用做参数,可以改变实参,大对象传参效率高,因为不需要再拷贝一份
void Swap(int& x,int& y)
{
int tmp = x;
x = y;
y = tmp;
}
//对实参无影响
void Swap(int x,int y)
{
int tmp = x;
x = y;
y = tmp;
}
如果使用传引用传参,函数内如果不改变参数,尽量使用const传参
做返回值
int& Num()
{
int n = 0;
n++;
return n;
}
这里的return n和上面讲的原理一样,返回的是n的拷贝参数,出了这个函数 n就被销毁了,也无法对n做出改变,典型的错误传引用返回,下面才是正确的使用方式
int& Num()
{
static int n = 0;//n在堆区,出了函数并不会被销毁
n++;
return n;
}
Num()++;//n++
接收时使用int& m = Num();
4.3 引用与指针的区别
5、 C和C++的相互调用
C++是兼容C的,因此可以相互调用,不过中间的过程需要一些调整
5.1 C++程序调用C库
我们以静态库的形式进行讲解
首先创建一个.c的项目文件,右击项目点击属性,将配置类型改为静态库,完成后F5运行
然后我们创建一个新的项目,创建.cpp文件,我们需要包含C项目的头文件
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//#include"Stack.h"//默认在当前目录下找,没有可以用相对路径或者绝对路径或者复制过来
//#include"../StackC++_C 7-9/StackC++_C 7-9/Stack.h"// ..表示上一层
大家可以自己操作一下,发现还是使用不了自己写的C库,观察编译器报错原因为链接错误link,这时我们需要右击.cpp项目,属性,常规,将附加库目录写入你的库文件所在路径
然后点击输入,将附加依赖项改为你的C项目生成的.lib的文件名字 ,完成后确定
我们再次运行自己的cpp程序,发现还是会发生链接错误,这又是什么原因?
在这里就涉及到了我们上面所讲的C++中独有的函数名规则,在C++调用C时,C里面是没有这个规则的,找的时候是用C++的方式找的,所以还是找不到,发生链接错误
不用慌,这是我们需要使用extern C,将我们的头文件包括里面
extern "C" {
#include"../StackC++_C 7-9/StackC++_C 7-9/Stack.h";
}
这时我们再次运行就可以通过啦,能通过的原因是在我们使用extern C后,C++会将函数按照C的风格来编译,因为C++兼容C。
5.2 C程序调用C++库
前面几部的流程和C++调用C类似,采取相反的操作即可,在这里就不再重复的赘述,有所改变的是在最后一步,C是不兼容C++的,我们只能去改CPP
还是使用extern C,下图是一种方式
另一种方式,将你的头文件括起来
//C++的宏
#ifdef __cplusplus//_ _有俩
extern "C" //在C里面编译就不显示了
{
#endif // _cplusplus
//初始化
void StackInit(ST* ps);
//插入
void StackPush(ST* ps, STDataType x);
//删除
void StackPop(ST* ps);
//销毁
void StackDestory(ST* ps);
//判空
bool StackEmpty(ST* ps);
//容量
int StackSize(ST* ps);
//返回栈顶元素
STDataType StackTop(ST* ps);
#ifdef __cplusplus
}
#endif // _cplusplus
6、内联函数
内联函数的作用我们可以参考宏的优点
#include <iostream>
using namespace std;
inline void f(int i);
void T(int i) {
cout << i << endl;
}
//.c文件
int main()
{
T(1);
return 0;
}
7、空指针
void TestPtr()
{
int* p1 = NULL;
int* p2 = 0;
}
NULL指针实际上是一个宏,在C中的头文件stddef.h中
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在 使用空值的指针时,都不可避免的会遇到一些麻烦,比如:
void f(int) {
cout<<"f(int)"<<endl;
}
void f(int*) {
cout<<"f(int*)"<<endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
return 0;
}
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。(32位4字节)
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。