智能指针

unique_ptr

这个智能指针是独占指针,私家车,给一块内存空间“加锁”,只有这个能调用。

unique_ptr可以转移不能赋值

注意声明用法;

get()函数是可以将unique_ptr的地址赋值给别人,返回unique_ptr的地址;虽然unique_ptr有独占性但是还是可以用这种方法操作

realse()函数返回unique_ptr的地址,但是会将unique_ptr的指针指向空,但是不释放内存空间

reset()函数unique_ptr的指针指向空,并且释放内存空间

int main()
{
	int* intP = new int(10);
	//声明智能指针
	std::unique_ptr<int>intPtr{std::make_unique<int>(100)};
	std::unique_ptr<int>intPtr1{ };
	//声明智能指针数组
	std::unique_ptr<int[]>intPtrarr{};
	std::cout << *intP << std::endl;
	std::cout << *intPtr << std::endl;
	std::cout << "get()函数使用" << std::endl;

	//get()
	int* p = intPtr.get();
	*p = 1000;
	

	std::cout << "realse()函数使用" << std::endl;
	//release()
	int* t = intPtr.release();
	std::cout << "t的数值为 " << *t << std::endl;
	std::cout << "t的地址为 " << t << std::endl;
	std::cout << "intPtr的地址为 " << intPtr << std::endl;
	//reset()
	intPtr.reset();
	system("pause");
	return 0;
}

move()函数

int main()
{
	int* intP = new int(10);
	//声明智能指针
	std::unique_ptr<int>intPtr{std::make_unique<int>(100)};
	std::unique_ptr<int>intPtr1{ };
	std::cout << "intPtr的数值为 " << *intPtr << std::endl;
	std::cout << "intPtr的地址为 " << intPtr << std::endl;
	std::cout << "intPtr1的地址为 " << intPtr1 << std::endl;
	std::cout << "交换后 " << std::endl;
	intPtr1 = std::move(intPtr);
	std::cout << "intPtr的地址为 " << intPtr << std::endl;
	std::cout << "intPtr1的数值为 " << *intPtr1 << std::endl;
	std::cout << "intPtr1的地址为 " << intPtr1 << std::endl;
	system("pause");
	return 0;
}

shared_ptr

shared_ptr具有共享性。可以有多个shared_ptr指向同一块内存空间,但是只有最后一个shared_ptr释放的时候才会释放掉这块内存空间。shared_ptr会保存当前地址有多少个智能指针调用

use.cout()可以统计当前内存空间有多少个指针调用

unique()返回一个bool类型的变量指示该指针是否是唯一的

reset()用法和unique_ptr不一样,这里是将指针设置为null,但是如果不是最后一个的话是不会释放内存空间的。

int main()
{
	int* intP = new int(10);
	std::shared_ptr<int>ptrA{ std::make_shared<int>(100) };
	std::shared_ptr<int>ptrB{  };
	std::cout << "ptrA的地址为 " << ptrA << std::endl;
	std::cout << "ptrB的地址为 " << ptrB << std::endl;
	ptrB = ptrA;
	std::cout << "ptrA的地址为 " << ptrA << std::endl;
	std::cout << "ptrB的地址为 " << ptrB << std::endl;
	std::shared_ptr<int>ptrE{  };
	std::shared_ptr<int>ptrC{  };
	std::shared_ptr<int>ptrD{  };
	ptrE = ptrA;
	ptrD = ptrA;
	ptrC = ptrA;
	std::cout << ptrB.use_count() << std::endl;
	if (ptrA.unique())
	{
		std::cout << "该指针是唯一的" << std::endl;
	}
	else
	{
		std::cout << "该指针不是唯一的" << std::endl;
	}
	std::shared_ptr<int>ptr123{ std::make_shared<int>(1000) };
	if (ptr123.unique())
	{
		std::cout << "该指针是唯一的" << std::endl;
	}
	else
	{
		std::cout << "该指针不是唯一的" << std::endl;
	}
	ptrA.reset();
	std::cout << "ptrA的地址为 " << ptrA << std::endl;
	return 0;
}

Union联合体

函数返回

第一种最简单的返回 函数运行在栈上,函数中生成的临时变量Role,在函数运行结束后会销毁。main函数中使用的Role实际上是临时变量的复制,将栈上的数据完完整整复制了一个Role过来。这样太占内存空间了


typedef struct Role
{
	char* name;
	int hp;
	int mp;
	int maxhp;
	int maxmp;
	int lv;
}*PRole,ROLE;


Role create(const char* s, int hp, int mp)
{
	Role t = { cstr(s),hp,mp,hp,mp,1 };
	return t;
}


int main()
{
	Role a = create("Niko", 100, 50);
	std::cout << a.name << std::endl;
	std::cout << a.hp << std::endl;
	std::cout << a.mp << std::endl;
	std::cout << a.maxhp << std::endl;
	std::cout << a.maxmp << std::endl;
	std::cout << a.lv << std::endl;
}

那如果我们使用指针呢?直接返回一个指针,这样就不用在生成一个Role了

这样我们生成Role的方式变成了new,生成在堆区。

PRole create(const char* s, int hp, int mp)
{
	PRole t = new Role{ cstr(s),hp,mp,hp,mp,1 };
	return t;
}


int main()
{
	Role* a = create("Niko", 100, 50);
	std::cout << a->name << std::endl;
	std::cout << a->hp << std::endl;
	std::cout << a->mp << std::endl;
	std::cout << a->maxhp << std::endl;
	std::cout << a->maxmp << std::endl;
	std::cout << a->lv << std::endl;
}

还有一种方法返回引用,返回的是地址。

Role& create(const char* s, int hp, int mp)
{
	PRole t = new Role{ cstr(s),hp,mp,hp,mp,1 };
	return *t;
}


int main()
{
	Role a = create("Niko", 100, 50);
	std::cout << a.name << std::endl;
	std::cout << a.hp << std::endl;
	std::cout << a.mp << std::endl;
	std::cout << a.maxhp << std::endl;
	std::cout << a.maxmp << std::endl;
	std::cout << a.lv << std::endl;
}

不定量参数

上次学的不定量参数是main函数中使用的 int main(int cnt,chararc[])

这里的不定量参数是运用到函数中的

教的很简单相当于一套模板固定搭配使用


int average(int cnt, ...)
{
	va_list(arg);
    //char *arg
    //arg 的使用会自增,这里的int是告诉他要用int类型去读取
	va_start(arg, cnt);
	int sum = 0;
	for (int i = 0; i < cnt; i++)
	{
		sum += va_arg(arg, int);
	}
	va_end(arg);
	return sum / cnt;

}


void main()
{
	std::cout << average(5,1,2,3,4,5) << std::endl;
}

左值右值

这里可以回头看看之前的课程

函数指针

函数的本质其实就是一连串的内存地址

函数名其实就是函数指针的变量名

这里我们给*padd这个指针 指向了add函数

函数模板重载 例外

这是因为传入的是地址,拿A和B的地址在进行比较

template<typename T>
T ave(T a, T b)
{
	return a > b ? a : b;
}





int main()
{
	int a = 101;
	int b = 110;
	int c = *ave(&a, &b);
	std::cout << c << std::endl;
	return 0;
}

定义一种例外情况就好

实际上这个template<>也可以删除掉,编译器肯定是先套其他有的函数最后才回去套模板

执行顺序函数重载一定大于函数模板



template<>
int * ave(int * a, int * b)
{
	return *a > *b ? a : b;
}




int main()
{
	int a = 101;
	int b = 110;
	int c = *ave(&a, &b);
	std::cout << c << std::endl;
	return 0;
}

函数模板依旧可以发生重载

template<typename T>
T ave(T a, T b)
{
	return (a + b) / 2;
}
template<typename T>
T ave(T a, T b,T c)
{
	return (a + b+c) / 2;
}

int main()
{
	int a = 101;
	int b = 110;
	int c = 220;
	std::cout << ave(a,b) << std::endl;
	std::cout << ave(a, b,c) << std::endl;
	return 0;
}

auto->decltype

auto

先简单说下auto的用法 自动类型推导 关键是typeid(a).name()函数可以显示出类型名 包括函数返回值也可以使用auto自动推导

int main()
{
	auto a = 20;
	auto b = 10.1;
	auto c = 'c';
	auto d = &a;
	std::cout << typeid(a).name() << std::endl;
	std::cout << typeid(b).name() << std::endl;
	std::cout << typeid(c).name() << std::endl;
	std::cout << typeid(d).name() << std::endl;
}

但是auto有缺点

  1. 不能保留const类型的属性

  2. 优先推导为值类型而不是引用

->拖尾函数

看下面这个例子

首先这段代码是正常执行的

auto bigger(int a, int b)
{
	return a > b ? a : b;
}

int main()
{
	int a = 100;
	int b = 10;
	auto x = bigger(a, b);
	std::cout << typeid(x).name() << std::endl;
	std::cout << x<< std::endl;
}

但是如果我们想要节省内存空间 想用引用传到a和b的值进入函数 并且之后修改值 可以看到出现报错了

这是因为上面的缺点2 如何解决? 我们知道要返回a或者b的地址才能进行修改,那么就是int&,所以但是由于缺点2不能返回引用所以我们使用->这个拖尾巴

auto bigger(int& a, int &b)->int&
{
	return a > b ? a : b;
}

这相当于我们强制其达到这种目的

decltype

高级版本的auto

int a=10;
unsigned int b = 10;
decltype(a - b)x;
const int a1 = 10;
decltype(a1)x1=100;
int& a2 = a;
decltype(a2)x2=a2;
int* a3 = &a;
decltype(a3)x3;

x自动推导为unsigned int. x1为const int 。 x2为int& . x3为int*

可以看到其完美解决了auto的缺点 但是auto和decltype有一点大区别 上面使用auto 会自动得到add函数的值给x1。 但是下面的x2指示帮你定义了x2的类型 这里不需要参数 直接给add让他自动推导就行了

auto x1 = add(10, 10);
decltype(add(10, 10))x2;

decltype得到数据类型有三原则

  1. 是否经历运算 如果有运算那么看运算结果是否有固定的内存地址(左值),如果有那么他就是有一你用

  2. 没有固定的内存地址 那么推导出来的类型就是该结果的类型

  3. 使用函数结果来推导时 函数返回什么结果decltype得到的就是什么结果 注意decltype()内的函数并不会真的运行给其赋值

int a = 10;
int b = 100;
int* pa = &a;
//a-b是运算  a-b是临时计算的变量 没有固定内存地址的数值 所以x就是int类型的值
decltype(a - b)x;
//*号也是运算符 *pa是指针有固定的内存地址 所以x1是int&
decltype(*pa)x1=a;
//[]这个也是运算符 同上
decltype(pa[0])x2=a;

可以利用auto解决上面的拖尾函数问题 a和b都是左值有固定的内存地址所以返回的是 int&

auto bigger(int& a, int &b)->decltype(a > b ? a : b)
{
	return a > b ? a : b;
}

在C++新特性中这种写法变成了

decltype(auto) bigger(int& a, int &b)
{
	return a > b ? a : b;
}

目前auto-decltype只需要了解 主要是和函数模板结合起来

int  main()
{

	int a = 10;;

	//先输出a在输出a++ 临时变量 所以x1是int
	decltype(a++)x1;
	//++a是有内存地址的所以x2 是int&
	decltype(++a)x2=a;
	std::cout << typeid(x1).name() << std::endl;
	std::cout << typeid(x2).name() << std::endl;
}

推断函数模板返回类型

static和incline

局部static只有局部可以访问 全局static哪里都可以访问 如图x在main函数中不能直接访问

内存中是和全局变量存放在一起的 生命周期和全局静态变量一样

static int xx = 100;

int add(int a, int b)
{
	static int x;
	x++;
	xx++;
	std::cout << x << std::endl;
	return a + b;
}



int main()
{
	add(1, 1);
	add(1, 1);
	add(1, 1);
	std::cout << xx << std::endl;

	return 0;
}

内联函数 关键字inline

作用 加快运行速度 提升效率

内联的意思是直接把内联函数的代码复制到调用的地方 减少函数调用的开销 成本是程序变大 函数被调用多次则复制多次

c++17开始改变了对inline的定义

静态成员函数是可以多次编译的 目前了解这么多吧

extern关键字

C语言不支持函数重载

C++如何调用C语言的代码? 例如C++的源文件是c.c,头文件声明是c.h。 c++源文件是c++.cpp,头文件是c++.h

那么我们在想在cpp文件中使用.c文件中的函数需要在c++.h的头文件声明中加上extern "C" 关键字

或者直接将用关键字加载头文件

但是.c文件如何调用C++的函数呢?

我们也可以使用extern关键字 在.CPP文件中 告诉编译器 这个函数要用C风格来编写 这样就可以

但是如果在c.h头文件声明加上关机键字则不行。在c.h中加上extern关键字,c++.cpp也要包含这个c.h,c.c也包含同一个头文件。但是C语言中是没有extern这个写法的。

还有一种方法

像ifdef__cplusplus #endif 这串声明 在cpp中是存在的,在.c中是不存在的.

如果在C++环境下呢 会把这串函数声明为extern "C"。 在Cpp下这串声明是有加了extern的在.c下是没有的

注意 如果声明函数为C语言风格,那么将不支持函数重载