- 函数的声明和定义都可以没有变量名
#include<iostream> #include<fstream> void n_chars(char, int) { printf("wer"); } int func(int, int); int main() { std::cout << func(1, 2) << std::endl; n_chars('a', 1); return 0; } int func(int a, int b) { return a + b; }
- CPP函数指针类似于CS的委托:
#include<iostream> #include<fstream> int (*ptrFunc1)(int, int); void (*ptrFunc2)(int, int); double (*ptrFunc3)(int (*ptrFunc)(int, int), int, int); int func1(int a, int b) { return a + b; } void func2(int a, int b) { std::cout << a + b << std::endl; } double fun3(int (*ptrFunc)(int, int),int num1,int num2) { return 2.0 * ptrFunc(num1, num2); } int main() { ptrFunc1 = func1; std::cout << ptrFunc1(1, 2) << std::endl; ptrFunc2 = func2; ptrFunc2(2, 3);//等于(*ptrFunc2)(2,3); ptrFunc3 = fun3; std::cout << ptrFunc3(ptrFunc1, 2, 3); return 0; }
3 5 10 10
- 下面代码块有关函数指针的声明中*pf(int)意味着pf()是一个返回指针的函数,而(*pf)(int)意味着pf是一个指向函数的指针。
double (*pf)(int); double *pf(int);
- 函数指针可以创建数组:
#include<iostream> #include<fstream> const double CalAdd(double a, double b) { return a + b; } const double CalMutiply(double a, double b) { return a * b; } const double CalSubstract(double a, double b) { return a - b; } const double (*ptrForCalculate[3])(double, double) { CalAdd, CalMutiply, CalSubstract }; int main() { int a, b; std::cin >> a >> b; for (auto ptr : ptrForCalculate) { std::cout << ptr(a, b) << std::endl; } return 0; }
4 5 9 20 -1
- 使用typedef可以简化函数指针名字引用:
#include<iostream> #include<fstream> const double CalAdd(double a, double b) { return a + b; } const double CalMutiply(double a, double b) { return a * b; } const double CalSubstract(double a, double b) { return a - b; } typedef const double (*ptrForCal)(double, double); int main() { ptrForCal ptrForAdd = CalAdd; std::cout << ptrForAdd(2, 3) << std::endl; ptrForCal ptrForMul = CalMutiply; std::cout << ptrForMul(2, 3) << std::endl; ptrForCal ptrForSub = CalSubstract; std::cout << ptrForSub(2, 3) << std::endl; return 0; }
- 引用变量必须在声明时进行初始化。
- 在函数参数传递中,
const int& a
和const int a
在语义上是有区别的。-
const int& a
是将参数a
声明为一个常量引用。这意味着参数a
是一个对传入的实参的引用,但不能通过a
来修改实参的值。这种方式可以避免复制大对象的开销,并且保证了传入的实参不会被修改。 -
const int a
是将参数a
声明为一个常量值。这意味着参数a
是传入实参的一个副本,而不是对实参的引用。在函数中对a
的修改不会影响原始的实参。 - 所以,
const int& a
和const int a
的区别在于,前者是对实参的引用,后者是对实参的副本。选择哪种方式取决于具体的需求,如果需要避免复制大对象或者需要修改实参的值,可以使用const int& a
,否则可以使用const int a
。
-
- CPP不允许表达式函数传参时候传递给没有被const修饰的引用变量,CPP会在必要时将const引用参数声明为临时变量,(尽量在对象这种复杂的数据类型用const typename& n_elem)。
#include<iostream> #include<fstream> int Func1(const int &a, const int &b) { return a + b; } int Func2(int& a) { return 2 * a; } int main() { int x = 2; std::cout << Func1(x + 2, x + 3) << std::endl;//ok std::cout << Func2(x + 2) << std::endl;//should not compile,don`t do it! return 0; }
- 函数返回值可以是引用变量,这样就不会把数据复制到一个新的临时的位置,效率会更高,但是引用变量尽量返回函数参数的的变量,引用尽量是自定义数据结构这种复杂的数据结构,而且返回值如果是引用类型尽量加const修饰。
- CPP可以使用模板来简化函数重载,当然模板也是可以重载的。
#include<iostream> template <typename T> void swap(T& a, T& b) { T tmp = a; a = b; b = tmp; } template <typename T> void swap(T a[], T b[], int size) { T temp; for (int i = 0; i < size; ++i) { temp = a[i]; a[i] = b[i]; b[i] = temp; } } int main() { int a(0), b{ 1 }; swap<int>(a, b); std::cout << a << " " << b << std::endl; swap(a, b); std::cout << a << " " << b << std::endl; int arr[]{ 1,2,3,4,5 }, brr[]{ 10,9,8,7,6 }; for (auto iter : arr) { std::cout << iter << " "; } std::cout << std::endl; for (auto iter : brr) { std::cout << iter << " "; } std::cout << std::endl; swap<int>(arr, brr, 5); for (auto iter : arr) { std::cout << iter << " "; } std::cout << std::endl; for (auto iter : brr) { std::cout << iter << " "; } std::cout << std::endl; swap(arr, brr, 5); for (auto iter : arr) { std::cout << iter << " "; } std::cout << std::endl; for (auto iter : brr) { std::cout << iter << " "; } std::cout << std::endl; }
1 0 0 1 1 2 3 4 5 10 9 8 7 6 10 9 8 7 6 1 2 3 4 5 1 2 3 4 5 10 9 8 7 6
- 模板使用的时候会产生一些无法具体化解决的限制,例如无法在没有运算符重载的情况下进行a+b的处理,CPP提供了显式具体化的方法。
#include<iostream> #include<fstream> struct Vector3 { double x; double y; double z; }; template <typename T> void Swap(T& a, T& b) { T temp = a; a = b; b = temp; } template<>void Swap<Vector3>(Vector3& a, Vector3& b) { Swap(a.x, b.x); Swap(a.y, b.y); Swap(a.z, b.z); } int main() { Vector3 startPos{ 2.0,3.0,4.0 }, endPos{ 5.0,6.0,7.0 }; std::cout << startPos.x << " " << startPos.y << " " << startPos.z << std::endl; std::cout << endPos.x << " " << endPos.y << " " << endPos.z << std::endl; Swap(startPos, endPos); std::cout << startPos.x << " " << startPos.y << " " << startPos.z << std::endl; std::cout << endPos.x << " " << endPos.y << " " << endPos.z << std::endl; }
- 对于重载函数的选择,编译器倾向于优先选择非模板函数,如果没有非模板函数,则倾向于选择从类型等各方面来说相对具体的函数,如果没有一个函数比其他函数更具体,或者没有与之相匹配的函数,则会产生错误,但是向下面的情况(第二行),编译器会倾向于选择模板函数,而不是非模板函数。
less(m,n); less<>(m,n);
- 针对于多个不同类型模板的数据相互作用产生新的模板的数据CPP定义了decltype(expression) var,其核对数据的类型顺序如下:
- 如果expression是没有用括号括起来的标识符,则var的类型与该标识符类型相同,如num1。
- 如果expression是个函数调用,则var类型与函数返回值相同,如num2,num3,num4。
- 如果expression是一个左值,则var指向其引用,如num5。
- 如果前面的条件都不满足,则var类型与expression相同,如num6。
- 如果需要多次声明,则可以结合typedef和decltype使用,如num7。
- 如果需要使用函数可以想Add函数一样使用,其中auto类型会去设置为decltype(x+y)类型。
#include<iostream> template<typename T1,typename T2> auto Add(T1 num1, T2 num2) -> decltype(num1 + num2) { return num1 + num2; } template<typename T1,typename T2> auto Multiply(T1 num1, T2 num2) -> decltype(num1 * num2) { return num1 * num2; } template<typename T1, typename T2> auto Substract(T1 num1, T2 num2) -> decltype(num1 - num2) { return num1 - num2; } int main() { int x = 5; double y = 10.5; decltype(x) num1 = x; decltype(Add(x, y)) num2 = Add(x, y); decltype(Multiply(x, y)) num3 = Multiply(x, y); decltype(Substract(x, y)) num4 = Substract(x, y); decltype((y)) num5 = y; decltype(x + y) num6 = x + y; typedef decltype(x + y) xpytype; xpytype num7 = x - y; std::cout << num5 << " " << y << std::endl; num5 = 20.5; std::cout << num1 << " " << num2 << " " << num3 << " " << num4 << " " << num5 << " " << num6 << " " << num7 << " " << y << std::endl; }
10.5 10.5 5 15.5 52.5 -5.5 20.5 15.5 -5.5 20.5
cpp primer plus笔记06-函数
于 2023-10-07 17:35:05 首次发布