跟随b站课程爱编程的大丙学习c++,进行记录1:原始字符串字面量它的主要作用:让字符串里的反斜杠、双引号和换行更容易书写,避免大量转义。基本语法是:R"(字符串内容)":std::string a = "F:\\robot\\config\\motor.yaml"; std::string b = R"(F:\robot\config\motor.yaml)";输出:F:\robot\config\motor.yaml#include <iostream> #include <string> using namespace std; int main() { int motor_id = 1; string path = R"(F:\robot\motor.yaml)"; cout << "Motor ID: " << motor_id << '\n'; cout << path << '\n'; return 0; }输出:2:constexprconstexpr变量:初始化值必须满足编译期常量表达式的要求。constexpr函数:满足条件时可以在编译期计算,也可以在运行期调用。先分清“编译期”和“运行期”编译期是把代码编译成程序的阶段;运行期是程序启动后执行代码的阶段。int a = 6; // 数字 6 不需要运行程序就能知道 int b; std::cin >> b; // b 的值需要等用户输入后才知道constexpr关心的就是:这个值能不能提前确定?理解const和constexpr变量const表示初始化后不能修改;constexpr变量在此基础上,还要求初始化值满足编译期常量表达式的条件。constexpr int count = 6; // 正确 constexpr int total = count * 2; // 正确,结果为 12如果值来自用户输入:int number; std::cin >> number; const int a = number; // 正确:得到值后不再修改 // constexpr int b = number; // 错误:编译期不知道 number注意:const int a = 6;也可以是编译期常量。区别是const不保证,而constexpr明确要求。理解constexpr函数constexpr int square(int x) { return x * x; }它可以用于编译期计算:constexpr int result = square(5); // 必须在常量表达式中得到 25也可以接受运行期的值:int number; std::cin >> number; int result = square(number); // 正确:运行期计算**函数写了constexpr,不代表每次调用都在编译期执行。**要看传入的值和调用的使用场景。放到函数模板中,含义不变模板负责让同一套代码支持不同类型:template<typename T> constexpr T square(T x) { return x * x; }constexpr int a = square(3); // T 是 int,得到 9 constexpr double b = square(1.5); // T 是 double,得到 2.25这里,template负责类型通用,constexpr负责允许满足条件时的编译期计算。模板在编译期处理,不代表函数调用一定在编译期执行。放到构造函数中,是为了支持编译期初始化对象构造函数负责给对象的成员设置初始值:struct Point { int x; int y; constexpr Point(int a, int b) : x(a), y(b) { } };其中,: x(a), y(b)表示用a初始化x,用b初始化y。constexpr Point p(3, 4); // 在编译期初始化对象 constexpr int value = p.x; // 得到 3这个构造函数也能创建普通对象:int number; std::cin >> number; Point p(number, 4); // 正确:运行期初始化对象构造函数上的constexpr提供编译期初始化的能力;声明对象时的constexpr则要求这次初始化必须满足编译期条件。3:auto自动推导auto的作用是:让编译器根据初始化表达式,推导变量的类型。类型确定后,就不会再改变。auto number = 10;这里相当于:int number = 10;3.1:const修饰指针指向的对象:会保留int number = 10; const int* p = &number; auto q = p; // q 的类型是 const int*const int*表示:不能通过这个指针修改它指向的对象。// *q = 20; // 错误:不能通过 q 修改 number q = nullptr; // 正确:q 自己可以改变指向3.2:const修饰指针本身:不保留const修饰指针本身:普通auto不保留注意const的位置变了:int number = 10; int* const p = &number; auto q = p; // q 的类型是 int*int* const表示:指针p自己不能改变指向。p不能修改指向,q可以修改指向// p = nullptr; // 错误:p 自己不能修改 q = nullptr; // 正确:q 自己可以修改原指针类型auto q = p;推导出的类型const int*const int*int* constint*const int* constconst int*3.3 auto&const int number = 10; const int& r = number;分别使用auto和auto&:auto a = r; // int:复制一个值 auto& b = r; // const int&:继续引用原对象a = 20; // 正确:a 是独立的、可以修改的 int // b = 20; // 错误:不能通过 b 修改原对象决定是否按引用推导的,是声明中的auto&,不是右边的变量原本是不是引用。auto&会根据被引用对象保留限定:int x = 10; const int y = 20; auto& a = x; // int& auto& b = y; // const int&3.4不能使用auto的场景场景错误写法原因普通局部变量没有初始化值auto x;没有依据,无法推导类型同一条声明推导出不同类型auto a = 1, b = 2.5;一个推导为int,另一个为double普通函数的参数〔C++17〕void show(auto x);C++17 不支持这种普通函数参数写法普通非静态数据成员struct Motor { auto id = 1; };此处必须明确成员类型直接代替容器元素类型std::vector<auto> values;必须提供具体元素类型不能声明数组auto arr[3]={1,2,3}原生数组应明确写出元素类型3.5常用的场景第一种:迭代器类型较长时。你已经学过map,可以比较下面两种写法:std::map<int, std::string> names; std::map<int, std::string>::iterator it1 = names.begin(); auto it2 = names.begin();it1和it2的类型相同。这里使用auto能省去冗长的类型名称。第二种:范围for循环。std::vector<std::string> names{"joint1", "joint2", "joint3"}; for (const auto& name : names) { std::cout << name << '\n'; }这里的name是const std::string&,可以读取元素,也避免了每次复制字符串。根据目的选择写法:循环写法含义for (auto value : values)每次复制一个元素for (auto& value : values)直接访问元素,可以修改for (const auto& value : values)直接访问元素,通过该引用只读第三种:保存 Lambda 表达式。后面学习回调函数时,会经常看到:auto square = [](int x) { return x * x; }; std::cout << square(3); // 输出 9Lambda 对象的具体类型由编译器生成,使用auto很方便。4:decltypedecltype负责确定类型,右侧初始化表达式负责提供值或决定引用绑定谁。4.1:基本用法:取得类型,不计算数值decltype(表达式)会分析表达式的类型,但不会实际执行其中的计算。int a = 10; decltype(a) b = 99; decltype(a + 3.14f) c = 52.13f; decltype(a + 1.2 * c) d = 520.1314;结果如下所示:int b = 99; float c = 52.13f; double d = 520.1314;4.2:直接写变量名:取得变量声明时的类型当decltype中直接写变量名时,会取得这个变量声明的类型,包括其中的const和引用。int x = 99; const int& y = x; decltype(x) a1 = x; decltype(y) b1 = x;相当于:int a1 = x;const int& b1 = x;4.3:函数调用函数返回方式对应调用的decltype结果返回intint返回int&int&返回int&&int&&返回const int&const int&int n = 100; int func_int(); int& func_int_r(); int&& func_int_rr(); const int func_cint(); const int& func_cint_r(); const int&& func_cint_rr(); const Test func_ctest(); decltype(func_int()) a2 = 0; // int decltype(func_int_r()) b2 = n; // int& decltype(func_int_rr()) c2 = 0; // int&& decltype(func_cint()) d2 = 0; // int decltype(func_cint_r()) e2 = n; // const int& decltype(func_cint_rr()) f2 = 0; // const int&& decltype(func_ctest()) g2 = Test(); // const Testdecltype(func_cint()) d2 = 0; // int decltype(func_ctest()) g2 = Test(); // const Test对于这里的内置类型int,按值返回时,调用表达式不会保留顶层const;而类类型const Test在这个例子中会保留。4.4表达式是一个左值,或者被括号( )包围,使用 decltype推导出的是表达式类型的引用(如果有const、volatile限定符不能忽略)#include <iostream> #include <vector> using namespace std; class Test { public: int num; }; int main() { const Test obj; //带有括号的表达式 decltype(obj.num) a = 0; decltype((obj.num)) b = a; //加法表达式 int n = 0, m = 0; decltype(n + m) c = 0; decltype(n = n + m) d = n; return 0; }obj.num 为类的成员访问表达式,符合场景1,因此 a 的类型为intobj.num 带有括号,符合场景3,因此b 的类型为 const int&。n+m 得到一个右值,符合场景1,因此c的类型为 intn=n+m 得到一个左值 n,符合场景3,因此d的类型为 int&5:返回值后置在编程中,在模板函数中需要知道返回类型:#include <iostream> using namespace std; // R->返回值类型, T->参数1类型, U->参数2类型 template <typename R, typename T, typename U> R add(T t, U u) { return t + u; } int main() { int x = 520; double y = 13.14; // auto z = add<decltype(x + y), int, double>(x, y); auto z = add<decltype(x + y)>(x, y); // 简化之后的写法 cout << "z: " << z << endl; return 0; }有时候并不知道函数内部具体返回是什么?可能会很复杂。具体格式:// 符号 -> 后边跟随的是函数返回值的类型 auto func(参数1, 参数2, ...) -> decltype(参数表达式)auto 会追踪 decltype() 推导出的类型#include <iostream> using namespace std; template <typename T, typename U> // 返回类型后置语法 auto add(T t, U u) -> decltype(t+u) { return t + u; } int main() { int x = 520; double y = 13.14; // auto z = add<int, double>(x, y); auto z = add(x, y); // 简化之后的写法 cout << "z: " << z << endl; return 0; }6:finalC++中增加了final关键字来限制某个类不能被继承,或者某个虚函数不能被重写如果使用final修饰函数,只能修饰虚函数,并且要把final关键字放到类或者函数的后面。6.1修饰函数如果使用final修饰函数,只能修饰虚函数,这样就能阻止子类重写父类的这个函数了:class Base { public: virtual void test() { cout << "Base class..."; } }; class Child : public Base { public: void test() final { cout << "Child class..."; } }; class GrandChild : public Child { public: // 语法错误, 不允许重写 void test() { cout << "GrandChild class..."; } };test()是基类中的一个虚函数,在子类中重写了这个方法,但是不希望孙子类中继续重写这个方法了,因此在子类中将test()方法标记为final,孙子类中对这个方法就只有使用的份了。6.2:修饰类使用final关键字修饰过的类是不允许被继承的,也就是说这个类不能有派生类。class Base { public: virtual void test() { cout << "Base class..."; } }; class Child final: public Base { public: void test() { cout << "Child class..."; } }; // error, 语法错误 class GrandChild : public Child { public: };7:overrid确保重新写的函数是正确的编译器都会指出:这个函数没有重写对应的基类虚函数。class Base { public: virtual void test() { cout << "Base class..."; } }; class Child : public Base { public: void test() override { cout << "Child class..."; } }; class GrandChild : public Child { public: void test() override { cout << "Child class..."; } };8:默认模板参数8.1类模板默认参数#include <iostream> using namespace std; template <typename T=int, T t=520> class Test { public: void print() { cout << "current value: " << t << endl; } }; int main() { Test<> t; t.print(); Test<int, 1024> t1; t1.print(); return 0; }8.2:函数模板默认参数#include <iostream> using namespace std; template <typename T=int> // C++98/03不支持这种写法, C++11中支持这种写法 void func(T t) { cout << "current value: " << t << endl; } int main() { func(100); return 0; }当所有模板参数都有默认参数时,函数模板的调用如同一个普通函数。但对于类模板而言,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随<>来实例化。9:函数指针先定义一个普通函数,用来计算两个整数的和:int add(int a, int b) { return a + b; }平时直接通过函数名调用:int result = add(2, 3); // result 是 5现在,我们用一个指针p指向这个函数:p是一个函数指针,指向的函数接收两个int,返回一个int。int (*p)(int, int) = add;部分含义(*p)p是一个指针后面的(int, int)它指向的函数接收两个int参数最前面的int这个函数返回int= add让p指向add函数有了p,就可以通过它调用add:int result = p(2, 3); // result 是 5因为p指向add,所以此时下面两种调用效果相同:add(2, 3); // 通过函数名调用 p(2, 3); // 通过函数指针调用完整代码:#include <iostream> int add(int a, int b) { return a + b; } int main() { int (*p)(int, int) = add; std::cout << p(2, 3) << '\n'; // 输出 5 return 0; }10:定义别名在c++中可以通过typedef重新定义以给类型typedef 旧的类型名 新的类型名;使用using定义别名的语法格式是这样的:using 新的类型 = 旧的类型; // 使用举例 using uint_t = int;定义函数指针:// 使用typedef定义函数指针 typedef int(*func_ptr)(int, double); // 使用using定义函数指针 using func_ptr1 = int(*)(int, double);定义模块:在定义模块别名中,使用typedef非常麻烦,在c++11中引入了usingtemplate <typename T> using mymap = map<int, T>;完整代码如下:using namespace std; template <typename T> using mymap = map<int, T>; int main(void) { // map的value指定为string类型 mymap<string> m; m.insert(make_pair(1, "luffy")); m.insert(make_pair(2, "ace")); // map的value指定为int类型 mymap<int> m1; m1.insert(1, 100); m1.insert(2, 200); return 0; }
跟随b站课程爱编程的大丙学习c++,进行记录1:原始字符串字面量它的主要作用:让字符串里的反斜杠、双引号和换行更容易书写,避免大量转义。基本语法是:R"(字符串内容)":std::string a = "F:\\robot\\config\\motor.yaml"; std::string b = R"(F:\robot\config\motor.yaml)";输出:F:\robot\config\motor.yaml#include <iostream> #include <string> using namespace std; int main() { int motor_id = 1; string path = R"(F:\robot\motor.yaml)"; cout << "Motor ID: " << motor_id << '\n'; cout << path << '\n'; return 0; }输出:2:constexprconstexpr变量:初始化值必须满足编译期常量表达式的要求。constexpr函数:满足条件时可以在编译期计算,也可以在运行期调用。先分清“编译期”和“运行期”编译期是把代码编译成程序的阶段;运行期是程序启动后执行代码的阶段。int a = 6; // 数字 6 不需要运行程序就能知道 int b; std::cin >> b; // b 的值需要等用户输入后才知道constexpr关心的就是:这个值能不能提前确定?理解const和constexpr变量const表示初始化后不能修改;constexpr变量在此基础上,还要求初始化值满足编译期常量表达式的条件。constexpr int count = 6; // 正确 constexpr int total = count * 2; // 正确,结果为 12如果值来自用户输入:int number; std::cin >> number; const int a = number; // 正确:得到值后不再修改 // constexpr int b = number; // 错误:编译期不知道 number注意:const int a = 6;也可以是编译期常量。区别是const不保证,而constexpr明确要求。理解constexpr函数constexpr int square(int x) { return x * x; }它可以用于编译期计算:constexpr int result = square(5); // 必须在常量表达式中得到 25也可以接受运行期的值:int number; std::cin >> number; int result = square(number); // 正确:运行期计算**函数写了constexpr,不代表每次调用都在编译期执行。**要看传入的值和调用的使用场景。放到函数模板中,含义不变模板负责让同一套代码支持不同类型:template<typename T> constexpr T square(T x) { return x * x; }constexpr int a = square(3); // T 是 int,得到 9 constexpr double b = square(1.5); // T 是 double,得到 2.25这里,template负责类型通用,constexpr负责允许满足条件时的编译期计算。模板在编译期处理,不代表函数调用一定在编译期执行。放到构造函数中,是为了支持编译期初始化对象构造函数负责给对象的成员设置初始值:struct Point { int x; int y; constexpr Point(int a, int b) : x(a), y(b) { } };其中,: x(a), y(b)表示用a初始化x,用b初始化y。constexpr Point p(3, 4); // 在编译期初始化对象 constexpr int value = p.x; // 得到 3这个构造函数也能创建普通对象:int number; std::cin >> number; Point p(number, 4); // 正确:运行期初始化对象构造函数上的constexpr提供编译期初始化的能力;声明对象时的constexpr则要求这次初始化必须满足编译期条件。3:auto自动推导auto的作用是:让编译器根据初始化表达式,推导变量的类型。类型确定后,就不会再改变。auto number = 10;这里相当于:int number = 10;3.1:const修饰指针指向的对象:会保留int number = 10; const int* p = &number; auto q = p; // q 的类型是 const int*const int*表示:不能通过这个指针修改它指向的对象。// *q = 20; // 错误:不能通过 q 修改 number q = nullptr; // 正确:q 自己可以改变指向3.2:const修饰指针本身:不保留const修饰指针本身:普通auto不保留注意const的位置变了:int number = 10; int* const p = &number; auto q = p; // q 的类型是 int*int* const表示:指针p自己不能改变指向。p不能修改指向,q可以修改指向// p = nullptr; // 错误:p 自己不能修改 q = nullptr; // 正确:q 自己可以修改原指针类型auto q = p;推导出的类型const int*const int*int* constint*const int* constconst int*3.3 auto&const int number = 10; const int& r = number;分别使用auto和auto&:auto a = r; // int:复制一个值 auto& b = r; // const int&:继续引用原对象a = 20; // 正确:a 是独立的、可以修改的 int // b = 20; // 错误:不能通过 b 修改原对象决定是否按引用推导的,是声明中的auto&,不是右边的变量原本是不是引用。auto&会根据被引用对象保留限定:int x = 10; const int y = 20; auto& a = x; // int& auto& b = y; // const int&3.4不能使用auto的场景场景错误写法原因普通局部变量没有初始化值auto x;没有依据,无法推导类型同一条声明推导出不同类型auto a = 1, b = 2.5;一个推导为int,另一个为double普通函数的参数〔C++17〕void show(auto x);C++17 不支持这种普通函数参数写法普通非静态数据成员struct Motor { auto id = 1; };此处必须明确成员类型直接代替容器元素类型std::vector<auto> values;必须提供具体元素类型不能声明数组auto arr[3]={1,2,3}原生数组应明确写出元素类型3.5常用的场景第一种:迭代器类型较长时。你已经学过map,可以比较下面两种写法:std::map<int, std::string> names; std::map<int, std::string>::iterator it1 = names.begin(); auto it2 = names.begin();it1和it2的类型相同。这里使用auto能省去冗长的类型名称。第二种:范围for循环。std::vector<std::string> names{"joint1", "joint2", "joint3"}; for (const auto& name : names) { std::cout << name << '\n'; }这里的name是const std::string&,可以读取元素,也避免了每次复制字符串。根据目的选择写法:循环写法含义for (auto value : values)每次复制一个元素for (auto& value : values)直接访问元素,可以修改for (const auto& value : values)直接访问元素,通过该引用只读第三种:保存 Lambda 表达式。后面学习回调函数时,会经常看到:auto square = [](int x) { return x * x; }; std::cout << square(3); // 输出 9Lambda 对象的具体类型由编译器生成,使用auto很方便。4:decltypedecltype负责确定类型,右侧初始化表达式负责提供值或决定引用绑定谁。4.1:基本用法:取得类型,不计算数值decltype(表达式)会分析表达式的类型,但不会实际执行其中的计算。int a = 10; decltype(a) b = 99; decltype(a + 3.14f) c = 52.13f; decltype(a + 1.2 * c) d = 520.1314;结果如下所示:int b = 99; float c = 52.13f; double d = 520.1314;4.2:直接写变量名:取得变量声明时的类型当decltype中直接写变量名时,会取得这个变量声明的类型,包括其中的const和引用。int x = 99; const int& y = x; decltype(x) a1 = x; decltype(y) b1 = x;相当于:int a1 = x;const int& b1 = x;4.3:函数调用函数返回方式对应调用的decltype结果返回intint返回int&int&返回int&&int&&返回const int&const int&int n = 100; int func_int(); int& func_int_r(); int&& func_int_rr(); const int func_cint(); const int& func_cint_r(); const int&& func_cint_rr(); const Test func_ctest(); decltype(func_int()) a2 = 0; // int decltype(func_int_r()) b2 = n; // int& decltype(func_int_rr()) c2 = 0; // int&& decltype(func_cint()) d2 = 0; // int decltype(func_cint_r()) e2 = n; // const int& decltype(func_cint_rr()) f2 = 0; // const int&& decltype(func_ctest()) g2 = Test(); // const Testdecltype(func_cint()) d2 = 0; // int decltype(func_ctest()) g2 = Test(); // const Test对于这里的内置类型int,按值返回时,调用表达式不会保留顶层const;而类类型const Test在这个例子中会保留。4.4表达式是一个左值,或者被括号( )包围,使用 decltype推导出的是表达式类型的引用(如果有const、volatile限定符不能忽略)#include <iostream> #include <vector> using namespace std; class Test { public: int num; }; int main() { const Test obj; //带有括号的表达式 decltype(obj.num) a = 0; decltype((obj.num)) b = a; //加法表达式 int n = 0, m = 0; decltype(n + m) c = 0; decltype(n = n + m) d = n; return 0; }obj.num 为类的成员访问表达式,符合场景1,因此 a 的类型为intobj.num 带有括号,符合场景3,因此b 的类型为 const int&。n+m 得到一个右值,符合场景1,因此c的类型为 intn=n+m 得到一个左值 n,符合场景3,因此d的类型为 int&5:返回值后置在编程中,在模板函数中需要知道返回类型:#include <iostream> using namespace std; // R->返回值类型, T->参数1类型, U->参数2类型 template <typename R, typename T, typename U> R add(T t, U u) { return t + u; } int main() { int x = 520; double y = 13.14; // auto z = add<decltype(x + y), int, double>(x, y); auto z = add<decltype(x + y)>(x, y); // 简化之后的写法 cout << "z: " << z << endl; return 0; }有时候并不知道函数内部具体返回是什么?可能会很复杂。具体格式:// 符号 -> 后边跟随的是函数返回值的类型 auto func(参数1, 参数2, ...) -> decltype(参数表达式)auto 会追踪 decltype() 推导出的类型#include <iostream> using namespace std; template <typename T, typename U> // 返回类型后置语法 auto add(T t, U u) -> decltype(t+u) { return t + u; } int main() { int x = 520; double y = 13.14; // auto z = add<int, double>(x, y); auto z = add(x, y); // 简化之后的写法 cout << "z: " << z << endl; return 0; }6:finalC++中增加了final关键字来限制某个类不能被继承,或者某个虚函数不能被重写如果使用final修饰函数,只能修饰虚函数,并且要把final关键字放到类或者函数的后面。6.1修饰函数如果使用final修饰函数,只能修饰虚函数,这样就能阻止子类重写父类的这个函数了:class Base { public: virtual void test() { cout << "Base class..."; } }; class Child : public Base { public: void test() final { cout << "Child class..."; } }; class GrandChild : public Child { public: // 语法错误, 不允许重写 void test() { cout << "GrandChild class..."; } };test()是基类中的一个虚函数,在子类中重写了这个方法,但是不希望孙子类中继续重写这个方法了,因此在子类中将test()方法标记为final,孙子类中对这个方法就只有使用的份了。6.2:修饰类使用final关键字修饰过的类是不允许被继承的,也就是说这个类不能有派生类。class Base { public: virtual void test() { cout << "Base class..."; } }; class Child final: public Base { public: void test() { cout << "Child class..."; } }; // error, 语法错误 class GrandChild : public Child { public: };7:overrid确保重新写的函数是正确的编译器都会指出:这个函数没有重写对应的基类虚函数。class Base { public: virtual void test() { cout << "Base class..."; } }; class Child : public Base { public: void test() override { cout << "Child class..."; } }; class GrandChild : public Child { public: void test() override { cout << "Child class..."; } };8:默认模板参数8.1类模板默认参数#include <iostream> using namespace std; template <typename T=int, T t=520> class Test { public: void print() { cout << "current value: " << t << endl; } }; int main() { Test<> t; t.print(); Test<int, 1024> t1; t1.print(); return 0; }8.2:函数模板默认参数#include <iostream> using namespace std; template <typename T=int> // C++98/03不支持这种写法, C++11中支持这种写法 void func(T t) { cout << "current value: " << t << endl; } int main() { func(100); return 0; }当所有模板参数都有默认参数时,函数模板的调用如同一个普通函数。但对于类模板而言,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随<>来实例化。9:函数指针先定义一个普通函数,用来计算两个整数的和:int add(int a, int b) { return a + b; }平时直接通过函数名调用:int result = add(2, 3); // result 是 5现在,我们用一个指针p指向这个函数:p是一个函数指针,指向的函数接收两个int,返回一个int。int (*p)(int, int) = add;部分含义(*p)p是一个指针后面的(int, int)它指向的函数接收两个int参数最前面的int这个函数返回int= add让p指向add函数有了p,就可以通过它调用add:int result = p(2, 3); // result 是 5因为p指向add,所以此时下面两种调用效果相同:add(2, 3); // 通过函数名调用 p(2, 3); // 通过函数指针调用完整代码:#include <iostream> int add(int a, int b) { return a + b; } int main() { int (*p)(int, int) = add; std::cout << p(2, 3) << '\n'; // 输出 5 return 0; }10:定义别名在c++中可以通过typedef重新定义以给类型typedef 旧的类型名 新的类型名;使用using定义别名的语法格式是这样的:using 新的类型 = 旧的类型; // 使用举例 using uint_t = int;定义函数指针:// 使用typedef定义函数指针 typedef int(*func_ptr)(int, double); // 使用using定义函数指针 using func_ptr1 = int(*)(int, double);定义模块:在定义模块别名中,使用typedef非常麻烦,在c++11中引入了usingtemplate <typename T> using mymap = map<int, T>;完整代码如下:using namespace std; template <typename T> using mymap = map<int, T>; int main(void) { // map的value指定为string类型 mymap<string> m; m.insert(make_pair(1, "luffy")); m.insert(make_pair(2, "ace")); // map的value指定为int类型 mymap<int> m1; m1.insert(1, 100); m1.insert(2, 200); return 0; }