C++学习笔记(7)-创新互联
若有一个函数需要返回两个字符串,有很多不同的方法可以实现。但在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、tuplevector和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、pairstd::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++的两种不同类型的内存:栈和堆。当程序开始的时候它被分成了一堆不同的内存区域(包括栈和堆),在应用程序启动后,操作系统将整个程序加载到内存并分配一堆物理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指令
#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到vector
#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函数:
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)-创新互联
URL地址:http://myzitong.com/article/dggosh.html