快上网专注成都网站设计 成都网站制作 成都网站建设
成都网站建设公司服务热线:028-86922220

网站建设知识

十年网站开发经验 + 多家企业客户 + 靠谱的建站团队

量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决

C++学习笔记(7)-创新互联

三十四、C++处理多返回值(还得是结构体/类)

若有一个函数需要返回两个字符串,有很多不同的方法可以实现。但在C++的默认情况下不能返回两种类型。

创新互联公司欢迎联系:18982081108,为您提供成都网站建设网页设计及定制高端网站建设服务,创新互联公司网页制作领域十年,包括成都发电机回收等多个领域拥有丰富的营销推广经验,选择创新互联公司,为企业锦上添花。1、结构体

若有个函数叫ParseShader需要返回两个字符串。可以选择的解决方法是:创建一个叫做ShaderProgramSource的结构体,它只包含这两个字符串。若还想返回一个整数或其他不同类型的东西,可以把它添加到结构体中并返回它。

struct ShaderProgramSource
{std::string VertexSource;
	std::string FragmentSource;
	int a;
}
static ShaderProgramSource ParseShader(const std::string& filepath)
{}
return {vs,fs};
2、数组

我们可以返回一个数组,就像返回一个std::string*指针。这是一个2个元素的数组,我们可以传入VertexSource或者FragmentSource。这不通用,因为必须要同一种类型。
在这里插入图片描述
还可以返回一个std::array,类型是string,大小是2。

#include 
static std::arrayParseShader(const std::string& filepath)
{}
return std::array(vs,fs);
///OR//
std::arrayresults;
results[0]=vs;
results[1]=fs;
return results;
3、vector

若返回两种以上则可以使用vector。它和array的区别是array在栈上创建,而vector会把它的底层存储在堆上,所以std::array会更快。

#includestatic std::vectorParseShader(const std::string& filepath)
{}
std::vectorresults;
results[0]=vs;
results[1]=fs;
return results;
4、tuple

vector和array只有在类型相同的情况下才有效。若类型发生变化,有两种方式,一种是tuple(元组),另一种是pair。

  • tuple基本上是一个类,它可以包含x个变量但它不关心类型;tuple在functional里面,utitly提供了make_tuple这样的工具。
#include//utitly提供了make_tuple工具
#include//tuple在functional里面
static std::tupleParseShader(const std::string& filepath)//增添int
{}
return std::make_pair(vs,fs);
std::tuplesources=ParseShader("res/shaders/Basic.shader");
//auto sources=ParseShader("res/shaders/Basic.shader");

从tuple里获取数据需要使用std::get;

std::string vs=std::get<0>(sources);

另一个例子:

#include#includeusing namespace std;

int main()
{tuplemy_tuple(1, 2.3, "hbh");

    int my_int;
    double my_double;
    string my_string;

    cout<< get<2>(my_tuple)<< endl;

    tie(my_int, my_double, my_string) = my_tuple;
    cout<< my_string<< endl;

    auto [x, y, z] = my_tuple;
    cout<< z<< endl;

    return 0;
}
5、pair

std::pair ,它与tuple的区别是返回值是2个字符串,也可以使用std::get或者sources.first,sources.second。

static std::pairParseShader(const std::string& filepath)
{}
三十五、模板template

当写一个函数里面使用模板,实际上创建了一个蓝本,因此当调用这个函数时,可以指定特定的参数,这个参数决定了放入到模板中的实际代码,也决定了如何使用这个函数。

#include#includetemplate//typename也可以写成class
void Print(T value)
{std::cout<< value<< std::endl;
}

int main()
{Print(5);//T替换为int
	Print("Hello");//T替换为string
	Print(5.5f);//T替换为float
	std::cin.get();
}
  • 模板函数不是一个真的函数,只有当我们实际调用它时,这些函数才会被真的创建,并作为源代码被编译。
  • 我们选择typename作为模板参数的类型,T是模板的参数名称。
  • 这里看上去是显示地指定类型,其实这个类型是隐式地从实际参数中得到的。
  • 若我们不写任何东西,完全没有使用模板函数,那么它就没有真正存在过。
Print(5);//调用Print使用尖括号指定类型

另一种用法:

#include#includetemplate//传入大小N
class Array
{private:
	int m_Array[N];
public:
	int Getsize() const {return N; }
};

int main()
{std::cin.get();
}

当我们调用这个Array时,指定一个Array大小为5,命名为array,这意味将第9和11行的N改为5。若调用array.Getsize然后打印到控制台,按下F5运行。
在这里插入图片描述模板可以包罗一切可以改变的参数:
在这里插入图片描述

三十六、C++的堆与栈内存

我们将讨论C++的两种不同类型的内存:栈和堆。当程序开始的时候它被分成了一堆不同的内存区域(包括栈和堆),在应用程序启动后,操作系统将整个程序加载到内存并分配一堆物理RAM以便程序可以运行。堆和栈是RAM中实际存在的两个区域,栈通常是一个预定义大小(2兆字节左右)的内存区域,堆也是一个预定义默认值的区域,但是它可以随着应用程序的进行而改变。

它们的不同之处在于如何为我们分配内存???
主要的区别是使用new关键字来分配内存。

#includestruct Vector3
{float x, y, z;
};

int main()
{//栈分配
	int value = 5;
	int array[5];

  //堆分配
	int* hvalue = new int;
	//这里使用的是new关键字,然而如果使用智能指针,用make_unique或者make_shared等函数,这都是一样的,它会为你调用new。
	*hvalue = 5;
	int* harray = new int[5];
	Vector3* hvector = new Vector3();//圆括号可选
	//需要delete你使用new分配的内存,智能指针可以为你做这个(释放内存)
	delete hvalue;
	delete []harray;
	delete hvector;
	
	std::cin.get();
}
  • 栈上分配的内存都挨着的,因为就是栈顶指针移动这么多字节;
  • 栈只是把东西堆在一起,所以速度很快;
  • 对于堆分配,则不会是紧挨着的;
  • 在堆上分配内存是一堆的事情,而在栈上分配内存,就像一条CPU指令
三十七、C++宏
#include//发生在编译器的预处理阶段
#define WAIT std::cin.get() 

int main()
{WAIT;
}
#include#define PR_DEBUG 0//需要添加到vs预处理器中

#if PR_DEBUG == 1//在Debug和Release模式下选中不同代码有效
#define LOG(x) std::cout<< x<< std::endl
#else
#define LOG(x)
#endif

int main()
{LOG("Hello");
	std::cin.get();
}

还可以使用反斜杠来写多行的宏,因为宏必须在同一行。

#include#define MAIN int main() \
{\
   std::cin.get();\
}

MAIN
三十八、auto关键字

auto可以让C++自动推导出数据的类型,不管是在创建还是初始化变量数据时,或是将一个变量对另一个变量进行赋值。

要注意的就是,要传引用时要在后面加&,比如const auto& ...
如下代码,用迭代器打印vector中的所有元素。

#include#include#includechar* GetName()
{return "Cherno";
}

int main()
{std::vectorstrings;
	strings.push_back("Apple");
	strings.push_back("Orange");

	for (std::vector::iterator it = strings.begin();it != strings.end();it++)
	{std::cout<< *it<< std::endl;
	}

	std::cin.get();
}

std::vectorstd::string::iterator是一个巨大的类型,我们可以写成auto让代码更具可读性。
在这里插入图片描述
若有DeviceManager和Device类,我们有一个从string到vectormap(映射),变量名叫m_Devices。

#include#include#include#includechar* GetName()
{return "Cherno";
}
 
 class Device{};

 class DeviceManager
 {private:
	 std::unordered_map>m_Devices;
 public:
	 const std::unordered_map>& GetDevices()const
	 { return m_Devices;
	 }
 };
int main()
{std::vectorstrings;
	strings.push_back("Apple");
	strings.push_back("Orange");

	for (auto it = strings.begin();it != strings.end();it++)
	{std::cout<< *it<< std::endl;
	}

	DeviceManager dm;
	const std::unordered_map>&   //类型
		devices=dm.GetDevices();
	std::cin.get();
}

我们可以用typedef或者using取个别名,来简化代码。

int main()
{std::vectorstrings;
	strings.push_back("Apple");
	strings.push_back("Orange");

	for (auto it = strings.begin();it != strings.end();it++)
	{std::cout<< *it<< std::endl;
	}
   
	using DeviceMap = std::unordered_map>;

	DeviceManager dm;
	const DeviceMap&   
		devices=dm.GetDevices();
	std::cin.get();
}

也可以使用auto;

int main()
{std::vectorstrings;
	strings.push_back("Apple");
	strings.push_back("Orange");

	for (auto it = strings.begin();it != strings.end();it++)
	{std::cout<< *it<< std::endl;
	}

	DeviceManager dm;
	const auto&  devices=dm.GetDevices();//若去掉const和&,将会制造一次复制操作
	std::cin.get();
}
三十九、C++静态数组(std array)

静态数组是指不增长的数组。如下代码,std::array中,第一个参数是array内数据的类型,第二个参数是array里面有多少个元素。

#include#include

int main()
{std::arraydata;
	data.size()
	data[0] = 2;
	data[4] = 1;
	std::cin.get();
}

使用std::array的好处是可以访问它的大小,它是一个类,并且它是在栈上创建的。

#include#include

void PrintArray(const std::array& data)
{for (int i = 0;i< data.size();i++)
	{std::cout<< data[i]<< std::endl;
	}
}
int main()
{std::arraydata;
	data[0] = 0;
	data[1] = 1;
	data[2] = 2;
	data[3] = 3;
	data[4] = 4;
	PrintArray(data);
	std::cin.get();
}
//如何传入一个标准数组作为参数,但不知道数组的大小?
#include#include

templatevoid PrintArray(const T& data)
{for (int i = 0;i< data.size();i++)
	{std::cout<< data[i]<< std::endl;
	}
}

int main()
{std::arraydata;
	data[0] = 0;
	data[1] = 1;
	data[2] = 2;
	data[3] = 3;
	data[4] = 4;
	PrintArray(data);
	std::cin.get();
}
四十、 C++的函数指针

原始风格的函数指针,来自于C语言。

函数指针,是将一个函数赋值给一个变量的方法。

#include#includevoid foo(std::string testStr)
{std::cout<< testStr<< std::endl;
}


int main()
{std::string testStr = "Hello!";

	// 第一种,auto
	auto foo1 = foo;
	foo1(testStr);

	// 第二种,函数指针变量
	void(*FuncPtrVariable)(std::string);
	FuncPtrVariable = foo;
	FuncPtrVariable(testStr);

	// 第三种,改变第二种的形式,表现的更自然
	typedef void(*FuncPtrType)(std::string);
	FuncPtrType FuncPtrTest = foo;
	FuncPtrTest("Test");

	std::cin.get();
}

一个更实际一点的例子:这相当于把func重命名为PrintValue,然后调用函数;
在这里插入图片描述
还可以用lambda函数:
在这里插入图片描述

四十一、C++的lambda函数

lambda本质上是我们定义一种叫做匿名函数的方式,我们用这种方式创建函数不需要实际创建一个函数,像是一个快速的一次性函数。lambda是我们不需要通过函数定义就可以定义一个函数的方法。

#include#includevoid ForEach(const std::vector& values, void(*func)(int))
{for (int value : values)
		func(value);
}

int main()
{std::vectorvalues = {1,5,4,2,3 };

	ForEach(values, [](int value) {std::cout<< " Value:"<< value<< std::endl;}); //lambda
	
	std::cin.get();
}

这里的函数指针定义了lambda需要做成什么样子,我们知道它会返回void(空),也知道它会接受一个int参数。这就是为什么这个函数什么也没有返回,它只是打印了一行文本。然后我们定义了int value参数,因为前面的函数指针需要一个int参数。

可以把lambda赋值给一个auto类型变量,然后将lambda变量传入函数。

#include#includevoid ForEach(const std::vector& values, void(*func)(int))
{for (int value : values)
		func(value);
}

int main()
{std::vectorvalues = {1,5,4,2,3 };
	
	auto lambda = [](int value) {std::cout<< " Value:"<< value<< std::endl;};//hear

	ForEach(values, lambda);
	
	std::cin.get();
}

若想把外部变量a放到lambda函数内部的指令中,我们可以值传递或者可以通过引用传递。[]就代表如何传递变量,若值传递则写上=,引用传递写上&,或者单独写上变量a。

auto lambda = [=](int value) {std::cout<< " Value:"<< a<< std::endl;};//值传递
auto lambda = [&](int value) {std::cout<< " Value:"<< a<< std::endl;};//引用
auto lambda = [a](int value) {std::cout<< " Value:"<< a<< std::endl;};//值传递
auto lambda = [&a](int value) {std::cout<< " Value:"<< a<< std::endl;};//引用

当我们试图传入某些变量时,不管是通过值还是引用来捕获变量,这里的ForEach都会出错,因为我们正在使用原始函数指针。若转变成std::function,返回void,有一个int参数叫做func就可以了。
在这里插入图片描述我们有一个可选的修饰符mutable,它允许函数体修改通过拷贝传递捕获的参数。若我们在lambda中给a赋值会报错,需要写上mutable。
在这里插入图片描述

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享文章:C++学习笔记(7)-创新互联
标题网址:http://6mz.cn/article/dggosh.html

其他资讯