• ue4c++【基础知识】


    1.函数指针

     编译时,系统会为每个函数分配一段存储空间,这段存储空间的首地址称为这个函数的地址,函数名表示这个地址。我们可以用指针变量来存放这个地址,这个指针变量就叫作函数指针变量,简称函数指针

    所以函数指针的定义方式为:

    函数返回值类型 (* 指针变量名) (函数参数列表);
    int(*p)(int, int);

    定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。p 的类型为 int(*)(int,int)。

    1. int Func(int x); /*声明一个函数*/
    2. int (*p) (int x); /*定义一个函数指针*/
    3. p = Func; /*将Func函数的首地址赋给指针变量p*/
    1. # include
    2. int Max(int, int); //函数声明
    3. int main(void)
    4. {
    5. int(*p)(int, int); //定义一个函数指针
    6. int a, b, c;
    7. p = Max; //把函数Max赋给指针变量p, 使p指向Max函数
    8. printf("please enter a and b:");
    9. scanf("%d%d", &a, &b);
    10. c = (*p)(a, b); //通过函数指针调用Max函数
    11. printf("a = %d\nb = %d\nmax = %d\n", a, b, c);
    12. return 0;
    13. }
    14. int Max(int x, int y) //定义Max函数
    15. {
    16. int z;
    17. if (x > y)
    18. {
    19. z = x;
    20. }
    21. else
    22. {
    23. z = y;
    24. }
    25. return z;
    26. }

    最后需要注意的是,指向函数的指针变量没有 ++ 和 -- 运算。

    2.引用

    编译器不会为引用开辟新的内存空间,它不是一个变量,而是已存在变量的一个别名。它与被引用的变量共用同一块内存空间。

    1. #include
    2. #include
    3. using namespace std;
    4. //定义左值引用并初始化为变量a,以16进制打印a、temp的地址
    5. int main()
    6. {
    7. int a = 23;
    8. int& temp = a;
    9. cout << "a:" << a << '\t' << "temp:" << temp << endl;
    10. cout << "&a:" << std::hex << &a << '\t' << "&temp:" << &temp << endl;
    11. system("pause");
    12. return 0;
    13. }

         引用的特点:

    1. 定义引用时必须给初始化
    2. 没有空引用
    3. 没有所谓的二级引用
    4. 一个变量可以有多个引用(就相当于一个变量有好几个别名,这是可以的)

    在这里插入图片描述

    关于常量引用:

    常引用实际上是一种万能引用 

    (1)引用普通变量,但是不能修改变量

    在这里插入图片描述

     (2)引用常量

    在这里插入图片描述

     (3)引用字面常量

    1. //常量引用的格式:const int &a。
    2. const int & a = 10;
    3. //引用字面常量时,分两步走,首先定义一个临时量 去引用临时量 不是引用真实的字面常量10。

    (4)引用其他类型的变量

    1. double dval = 3.14;
    2. const int& a= dval;
    3. //实际情况为编译器生成了一个临时变量b,常量引用绑定的是临时变量b。
    4. //const int b = dval;
    5. //const int&a =b;

    总结

    1. int main(){
    2.   double dval = 3.14159;
    3.   const int &ir = 1024; //仅对const引用才是合法的
    4.   const int &ir2 = dval; //仅对const引用才是合法的
    5.   const double &dr = dval + 1.0; //仅对const引用才是合法的
    6. }

    关于对数组的引用:

    在引用数组时,必须知道数组的大小

    1. int main()
    2. {
    3. int a = 10;
    4. int b = 10;
    5. int ar[5] = { 1,2,3,4,5 };
    6. int& x = ar[0]; //ok
    7. int(&x)[5] = ar; //ok 没有[5]无法编译通过
    8. return 0;
    9. }

    关于对指针的引用:

    1. int main()
    2. {
    3. int a = 100;
    4. int *p = &a;
    5. int * &rp = p;
    6. cout << a << endl;
    7. cout << *p << endl;
    8. cout << *rp << endl; //这里为什么要将*放在前面,因为p的类型是 int * 作为一个整体哦!!
    9. cout << p << endl;
    10. cout << rp << endl;
    11. getchar();
    12. return 0;
    13. }
    14. /*
    15. 100
    16. 100
    17. 100
    18. 012FF84C
    19. 012FF84C
    20. */

    引用作为返回值:

    当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。

    1. #include
    2. using namespace std;
    3. double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
    4. double& setValues(int i) {
    5. double& ref = vals[i];
    6. return ref; // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i]
    7. }
    8. // 要调用上面定义函数的主函数
    9. int main ()
    10. {
    11. cout << "改变前的值" << endl;
    12. for ( int i = 0; i < 5; i++ )
    13. {
    14. cout << "vals[" << i << "] = ";
    15. cout << vals[i] << endl;
    16. }
    17. setValues(1) = 20.23; // 改变第 2 个元素
    18. setValues(3) = 70.8; // 改变第 4 个元素
    19. cout << "改变后的值" << endl;
    20. for ( int i = 0; i < 5; i++ )
    21. {
    22. cout << "vals[" << i << "] = ";
    23. cout << vals[i] << endl;
    24. }
    25. return 0;
    26. }
    改变前的值
    vals[0] = 10.1
    vals[1] = 12.6
    vals[2] = 33.1
    vals[3] = 24.1
    vals[4] = 50
    改变后的值
    vals[0] = 10.1
    vals[1] = 20.23
    vals[2] = 33.1
    vals[3] = 70.8
    vals[4] = 50

    3.模板

    泛型编程是编写与类型无关的逻辑代码,是实现代码复用的一种手段,其中模板是泛型编程的基础

    模板:模板包含函数模板和类模板

    (1)函数模板:代表了一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本 。

            格式:

    1. template<typename T1,typename T2,...>
    2. 返回值类型 函数名(参数列表)
    3. {
    4. ...
    5. }
    6. //typename是定义模板函数的关键字,在这里也可以使用class,
    7. //不可使用struct,为了便于区分,建议尽量使用typename,
    8. //使用class容易与C++中的类混淆。

           例子:

    1. #include
    2. using namespace std;
    3. template<class T>
    4. void Swap(T & x, T & y)
    5. {
    6. T tmp = x;
    7. x = y;
    8. y = tmp;
    9. }
    10. int main()
    11. {
    12. int n = 2, m = 8;
    13. Swap(n, m); //编译器自动生成 void Swap (int &, int &)函数
    14. cout<<"n="<" "<<"m="<
    15. double f = 2.8, g = 8.2;
    16. Swap(f, g); //编译器自动生成 void Swap (double &, double &)函数
    17. cout<<"f="<" "<<"g="<
    18. char *a = "abc",*b = "def"; //编译器自动生成 void Swap (char &, char &)函数
    19. Swap(a,b);
    20. cout<<"a="<" "<<"b="<
    21. return 0;
    22. }

     模板函数的特殊化:

    尽管模板函数能够对多种类型值进行操作,但是不同类型值在有些操作时需要先进行一定处理即特化,比如字符串是无法直接进行大小比较的,但我们可以比较其字符串长短,下面我们以Compare函数为例。

    1. #include
    2. #include
    3. using namespace std;
    4. //函数模板
    5. template<class T>
    6. bool Compare(T t1,T t2){
    7. return t1==t2;
    8. }
    9. template<> //函数模板特化
    10. bool Compare(char *t1,char *t2){
    11. return strcmp(t1,t2)==0;
    12. }
    13. int main(int argc, char* argv[])
    14. {
    15. int a = 2;
    16. int b = 2;
    17. int c = 8;
    18. double d = 2.8;
    19. double e = 2.8;
    20. double f = 8.2;
    21. char str1[] = "abc";
    22. char str2[] = "abc";
    23. char str3[] = "def";
    24. cout<<Compare(a,b)<
    25. cout<<Compare(a,c)<
    26. cout<<Compare(d,e)<
    27. cout<<Compare(e,f)<
    28. cout<<Compare(str1,str2)<
    29. cout<<Compare(str1,str3)<
    30. return 0;
    31. }
    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. /*
    6. 1.自动生成一个随机数组
    7. 2.对其进行选择排序
    8. 3.使用模板函数进行不同类型数据的排序
    9. 如:整型,浮点型,字符型,结构体类型(自定义)
    10. */
    11. //定义模板函数
    12. template <typename T>
    13. void SelectSort(T a[],int n){
    14. for(int i=0;i
    15. int min_index=i;
    16. for(int j=i+1;j
    17. if(a[j]
    18. min_index=j;
    19. swap(a[i],a[min_index]);
    20. }
    21. }
    22. template <typename P>
    23. void PrintArray(P a[],int n){
    24. for(int i=0;i
    25. cout<" ";
    26. }
    27. cout<
    28. }
    29. //随机数组
    30. int* RandomArray(int n,int rangeL,int rangeR){
    31. int *arr=new int[n];//创建一个大小为n的数组
    32. srand(time(NULL));//以时间为"种子"产生随机数
    33. for(int i=0;i
    34. arr[i]=rand()%(rangeR-rangeL+1)+rangeL;//生成指定区间[rangeL,rangeR]里的数
    35. }
    36. return arr;
    37. }
    38. int main(){
    39. float b[5]={0.5,2.7,1.5,15.8,10.2};
    40. char c[5]={'e','a','c','d','b'};
    41. int n;
    42. cout<<"请输入数据规模n:";cin>>n;
    43. int* a=RandomArray(n,1,100);//生成[1,100]内的随机数组成的数组
    44. cout<<"整型排序:";SelectSort(a,n); PrintArray(a,n);//整型
    45. cout<<"浮点型排序:";SelectSort(b,5); PrintArray(b,5);//浮点型
    46. cout<<"字符型排序:";SelectSort(c,5); PrintArray(c,5);//字符型
    47. return 0;
    48. }

    (2)模板类:如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就可以将此类声明为模板,它的存在不是代表一个具体的、实际的类

    例子:

    1. template <typename T>
    2. class A
    3. {
    4. public:
    5. A(T t){
    6. this->t = t;
    7. }
    8. T& getT(){
    9. return t;
    10. }
    11. private:
    12. T t;
    13. };
    1. #include
    2. using namespace std;
    3. template <typename T>
    4. class A
    5. {
    6. public:
    7. //函数的参数列表使用虚拟类型
    8. A(T t = 0){
    9. this->t = t;
    10. }
    11. //成员函数返回值使用虚拟类型
    12. T& getT(){
    13. return t;
    14. }
    15. private:
    16. //成员变量使用虚拟类型
    17. T t;
    18. };
    19. void printA(A<int>& a) {
    20. cout << a.getT() << endl;
    21. }
    22. int main(void) {
    23. //1.模板类定义类对象,必须显示指定类型
    24. //2.模板种如果使用了构造函数,则遵守以前的类的构造函数的调用规则
    25. A<int> a(666);
    26. cout << a.getT() << endl;
    27. //模板类做为函数参数
    28. printA(a);
    29. system("pause");
    30. return 0;
    31. }

    模板类的全特化和偏特化

    全特化:对所有模板类型进行特化

    偏特化:对模板类型做一些限制特化

    1. #include //全特化
    2. using namespace std;
    3. template<typename T1, typename T2>
    4. class A{
    5. public:
    6. void function(T1 value1, T2 value2){
    7. cout<<"value1 = "<
    8. cout<<"value2 = "<
    9. }
    10. };
    11. template<>
    12. class A<int, double>{ // 类型明确化,为全特化类
    13. public:
    14. void function(int value1, double value2){
    15. cout<<"intValue = "<
    16. cout<<"doubleValue = "<
    17. }
    18. };
    19. int main(){
    20. A<int, double> a;
    21. a.function(28, 28.8);
    22. return 0;
    23. }
    1. #include //偏特化
    2. using namespace std;
    3. template<typename T1, typename T2>
    4. class A{
    5. public:
    6. void function(T1 value1, T2 value2){
    7. cout<<"value1 = "<
    8. cout<<"value2 = "<
    9. }
    10. };
    11. template<typename T>
    12. class Adouble>{ // 部分类型明确化,为偏特化类
    13. public:
    14. void function(T value1, double value2){
    15. cout<<"charValue = "<
    16. cout<<"doubleValue = "<
    17. }
    18. };
    19. int main(){
    20. A<char, double> a;
    21. a.function('a', 28.8);
    22. return 0;
    23. }

    1.父类一般类,子类是模板类, 和普通继承的玩法类似

    2.子类是一般类,父类是模板类,继承时必须在子类里实例化父类的类型参数

    3.父类和子类都时模板类时,子类的虚拟的类型可以传递到父类中

    1. class A {
    2. public:
    3. A(int temp = 0) {
    4. this->temp = temp;
    5. }
    6. ~A(){}
    7. private:
    8. int temp;
    9. };
    10. template <typename T>
    11. class B :public A{
    12. public:
    13. B(T t = 0) :A(666) {
    14. this->t = t;
    15. }
    16. ~B(){}
    17. private:
    18. T t;
    19. };
    1. class A {
    2. public:
    3. A(int temp = 0) {
    4. this->temp = temp;
    5. }
    6. ~A(){}
    7. private:
    8. int temp;
    9. };
    10. template <typename T>
    11. class B :public A{
    12. public:
    13. B(T t = 0) :A(666) {
    14. this->t = t;
    15. }
    16. ~B(){}
    17. private:
    18. T t;
    19. };
    1. template <typename T>
    2. class A {
    3. public:
    4. A(T t = 0) {
    5. this->t = t;
    6. }
    7. ~A(){}
    8. private:
    9. T t;
    10. };
    11. class B:public A<int> {
    12. public:
    13. //也可以不显示指定,直接A(666)
    14. B(int temp = 0):A<int>(666) {
    15. this->temp = temp;
    16. }
    17. ~B() {}
    18. private:
    19. int temp;
    20. };

    4宏

    首先说一下预处理指令:

    以#开头的行,都是预处理指令,用于指示编译器做一些预处理工作。例如#include  "xxx.h"

    预处理指令不是语句,结尾不用加分号

    其实宏就是在源码在编译前一个预处理指令,将代码中指定宏命令找到并以文本的形式进行替换而已。

     1)使用宏定义常量    示例:#define MAXSIZE 200

    1. #include
    2. #define M 20
    3. int main()
    4. {
    5. int a = M;
    6. int b = 20;
    7. /*
    8. 编译后变成以下代码
    9. int a = 20;
    10. int b = 20;
    11. */
    12. printf("%d %d",a,b);
    13. return 0;
    14. }

    这里写图片描述

       2)使用宏定义表达式  示例:define MAX(a,b) ((a)<(b)?(b):(a))

     

    额外加一个例子

    1. #define LOG(x) std::cout << x << std::endl
    2. int main(int argc, char const *argv[])
    3. {
    4. LOG("hello");
    5. std::cin.get();
    6. }

    3) 使用宏来调用多个语句  示例播放mp3文件,并打印语句

     

    4)使用宏定义函数     

    1. #include
    2. #include
    3. #define MAIN \
    4. int main() \
    5. { \
    6. std::cin.get(); \
    7. }
    8. MAIN

     还有一个重要的点就是宏的条件编译

    1)如果没有定义debug,就定义一下

    2)如果定义了MY_MAX,我们就定义MY_MAX_EX

     3)  如果存在PR_DEBUG就将LOG(x) 定义为 std::cout << x << std::endl进行输出。否则定义为空不进行任何输出。

    1. #include
    2. #include
    3. #ifdef PR_DEBUG
    4. #define LOG(x) std::cout << x << std::endl
    5. #else
    6. #define LOG(x)
    7. #endif
    8. int main(int argc, char const *argv[])
    9. {
    10. LOG("hello");
    11. std::cin.get();
    12. }

    5虚函数

    那些被virtual关键字修饰的成员函数,就是虚函数

    其作用就是实现多态性  

    例子:

    1. #include
    2. using namespace std;
    3. class A
    4. {
    5. public:
    6. void print()
    7. {
    8. cout<<"This is A"<
    9. }
    10. };
    11. class B : public A
    12. {
    13. public:
    14. void print()
    15. {
    16. cout<<"This is B"<
    17. }
    18. };
    19. int main()
    20. {
    21. //为了在以后便于区分,我这段main()代码叫做main1
    22. A a;
    23. B b;
    24. a.print();
    25. b.print();
    26. return 0;
    27. }

    输出:分别是“This is A”、“This is B”。

    此时我们修改main函数

    1. int main()
    2. {
    3. //main2
    4. A a;
    5. B b;
    6. A *p1 = &a;
    7. A *p2 = &b;
    8. p1->print();
    9. p2->print();
    10. return 0;
    11. }

    输出:两个“This is A”

    问题来了,p2明明指向的是class B的对象但却是调用的class A的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数。

    1. class A
    2. {
    3. public:
    4. virtual void print(){cout<<"This is A"<
    5. };
    6. class B : public A
    7. {
    8. public:
    9. void print(){cout<<"This is B"<
    10. };

    毫无疑问,class A的成员函数print()已经成了虚函数,那么class B的print()成了虚函数了吗?回答是Yes,我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。所以,class B的print()也成了虚函数。那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了(语法上可加可不加,不加的话编译器会自动加上,但为了阅读方便和规范性,建议加上)。

    修改后的输出结果:This is A和This is B

  • 相关阅读:
    ERROR in static/js/xxx.js from UglifyJs Unexpected token name «currentVersion»
    Redis之事务
    win10下用cmake编译zlib并验证
    大数据Flink(九十):Lookup Join(维表 Join)
    智慧城市安全监控的新利器
    2022年全国大学生数学建模竞赛A题思路
    docker简介和安装
    AI芯片架构体系综述:芯片类型CPU\GPU\FPGA\ASIC以及指令集CSIS\RISC介绍
    第十章 STL
    “2024杭州智慧城市及安防展会”将于4月在杭州博览中心盛大召开
  • 原文地址:https://blog.csdn.net/zhang2362167998/article/details/126362470