c++学习笔记

本文详细介绍了C++中的析构函数,包括系统默认和自定义的执行顺序,以及虚函数的概念和特性。讨论了类大小与虚函数表的关系,以及sizeof操作符的应用。同时,讲解了静态成员函数与虚函数的区别,以及如何在静态函数中访问非静态成员。此外,还涵盖了C++中浮点数、强制类型转换、const关键字的使用、多态性原理和设计模式的基础知识。
摘要由CSDN通过智能技术生成
  1. 本博客的技术规范全部是基于c++而言。频出妙手不如减少失误。
  2. 析构函数:c++中,即使自定义了析构函数,系统仍然会提供一个默认的析构函数,无参,空函数体,并且在对象作用域结束时,先调用系统提供的析构函数,后调用自定义的析构函数
  3. virtual函数:virtual函数只能定义在类内,不能将普通函数定义为virtual函数
  4. 类的大小:指针类型变量在32位操作系统下所占内存大小是4个字节,64位下占八个字节。有虚函数的类,会有一个指向虚函数表的指针,导致类的大小增加4个字节(32位操作系统)。一个没有任何成员的类,大小为1。有一个整形变量,大小为4,而不是1+4。有成员变量的类,每增加一个成员变量就增加该变量大小。所以有虚函数的类,大小是所有成员变量加4。普通成员函数不会增加类的大小。
  5. sizeof操作符:可以用于求一个类型的大小也可以用于求某个具体变量的大小。
  6. 虚函数指针:在类中会有指向虚函数表的指针,在对象中也会有。每个对象会有自己的指向虚函数表的指针。
  7. 在类的静态函数中,不能访问一个没有所属对象的类非静态成员变量。静态成员函数中,没有this指针,因为静态函数不单独属于某一个对象,而是属于整个类。如何在静态函数中访问非静态成员?将一个对象作为实参传入静态函数,通过这个实例就可以访问非静态成员。
  8. typedef void(*Fun)(void);
    //定义一个参数和返回值均为空的函数指针类型
  9. 静态函数(或者是普通函数)与虚函数的区别:从运行角度的理解,静态函数的入口地址,是在编译时确定的,如果给定一个指向空的类指针,用这个指针也可以调用到静态函数(或者是普通函数)。但是不能用这个指针,调用到虚函数,原因是虚函数的调用,是通过对象中一个指向虚函数表的指针来完成的。如果不创建一个类的对象,就无法拿到实例中指向虚函数表的指针。注意,虚函数表,即使不创建对象,也是存在的,只不过这时你无法找到指向虚函数表的指针,也就无法调用他。虚函数表是一个指针数组,其中的元素指向虚函数的入口。虚函数表在编译期间创建,其中的各个元素,值都是固定的,也就是说虚函数的入口,也是固定的。各个对象,共享一张虚函数表。从这个角度,也可以理解为什么有虚函数的类大小要加4。因为有一个用于指向虚函数表指针成员,不过没有为这个成员分配空间,更没有赋值。
  10. 使用虚函数表中的元素来访问虚函数,一般指向虚函数表的指针地址与类实例的首地址相同。
    class Kevin{
    public:
          virtual void  kevinf2(){
                std::cout<<"virtual function"<<std::endl;
          }
          void kevinf1(){
                std::cout<<"kevin"<<std::endl;
          }
    };
    
    void 
    int main(){
          typedef void(*Func)(void);
          Kevin *p1=new Kevin();
          Kevin *p2=new Kevin();
          long *op1=(long*)p1;
          long *op2=(long*)p2;
          long *virp1=(long*)*op1;
          long *virp2=(long*)*op2;
          std::cout<<virp1<<std::endl;
          std::cout<<virp2<<std::endl;
          Func f1=(Func)virp1[0];
          Func f2=(Func)virp2[0];
          f1();
          f2();
          return 0;
    }

     

  11. 浮点数:在32位机器中,单精度用4个字节(32位)来表示一个实数,双精度用8个字节(64)位来表示一个实数,所以二者所表示的实数范围不同,小数点后能精确到的位数也不同。单精度能表示8个有效位,双精度可以表示16个有效位。所谓浮点,相对定点而言,即小数点的位置不固定,定点数相当于一种特殊的浮点数,即小数点的位置固定在数组末尾。
  12. c++中试用强制类型转换,要用()将被转换的变量括起来。
  13. 强制转换之static_cast:c++static_cast强制转换用于父类和子类之间的转换,可以子转父,是类型安全的,也可以父转子,但类型安全不能保证。但只能是子类或者是父类的指针类型之间的转换,不能是单纯的非指针的实例。
  14. c++中四种基本数据类型:整型、浮点型、字符型、布尔型
  15. 强制转换之static_cast:整型转字符型:转成该整型ascii码对应的字符,字符转整型:转换成该字符对应的ascii码。整型转布尔型:非0转为1(true),0转为0(false)。
  16. 强制转换之dynamic_cast:用于将父类指向子类实例的指针或者是引用强转为子类类型,或者是用于将子类类型强转为父类类型。转化分两种情况:父类中有虚函数和没有虚函数。这两种情况,子类指针转换为父类指针都可以成功。没有虚函数时,将父类指针转化为子类指针会报错。有虚函数时,将父类指向子类实例的指针强转为子类指针转化成功,父类指像父类实例的指针,即实际上没有指向子类的指针,强转为子类指针时会返回一个空指针。这也是danamic_cast与static_cast的最大不同之处,dynamic_cast会真正地使转换是安全的。
  17. 强制转换之static_cast和dynamic_cast:总结起来,static_cast有三种基本使用场景:类层次之间转换(向下转型不保证安全),c++基本数据类型之间转换,从空指针转换到目标类型的转换。dynamic_cast主要有一种使用场景(还有其他使用场景):类层次之间的转换,是保证类型安全的。
  18. 将类的实例声明为const对象,那么不能用该对象可以调用任何成员变量,但是不能修改成员变量的值。该对象只能调用函数末尾有const关键字的成员函数,因为任何末尾无const的成员函数都可能修改成员变量的值。如果一定要通过末尾有const的成员函数或者是const对象来修改成员变量的值,那么需要用mutable关键字来修饰成员变量。
  19. c++中,const int* ptr1=&var,那么ptr1不能指向其他变量,但是var的值可以通过*ptr1来改变。int* const ptr1=&var1,那么ptr1所指向变量的值,不能通过*ptr1来改变,但是该指针可以指向其他变量。任何常指针,指向一个普通变量,这个变量都是可以通过直接赋值改变自己的值的。
  20. 强制转换之const_cast:使用场景:
        int const var1 = 10;
        int * ptr1 = const_cast<int*>(&var1);
        *ptr1=20;
        std::cout<<var1<<std::endl;//输出:10,编译器直接将10替换为了var1,没有读内存中的值,编译器做了优化
        std::cout<<*ptr1<<std::endl;//输出:20,是从内存中读取的值
    
        int const var2 =20;
        int &refe1=const_cast<int&>(var2);
        refe1++;
        std::cout<<var2<<std::endl;//输出21
        std::cout<<refe1<<std::endl;//输出21
    

    在用const指针(int const *p而不是int* const p)指向一个const变量的时候,通过const_cast来将const指针转化为普通指针,并用改变类型后的指针来对原来const 变量所在的内存进行值的改变。强转指针,改变值后,编译器会做优化,强转引用改变值后,改变的是多少就是多少。通过const_cast是可以改变一个常量的值的。const_cast的作用:通过强转常指针来改变一个常量的值

     

     

  21. c++中指针变量以16进制存储,可以用Long将其转为十进制。
  22. 用<<std::hex<<可以将十进制变量作为十六进制变量进行输出。
  23. 强制转换之reinterpret_cast:可以在整型和指针之间相互转换,可以在任意指针之间进行转换
        int var1=10;
        long* ptr1=reinterpret_cast<long*>(var1);
        //int var2=reinterpret_cast<int>(ptr1);
        char var2='a';
        char *ptr2=&var2;
        long *ptr3=reinterpret_cast<long*>(ptr2);

     

  24. c++中的fork函数是创建一个与当前进程完全相同的子进程,子进程从fork()处开始运行,父进程的fork()返回的是子进程的id,子进程的fork()返回的是0。
  25. static关键字:修饰局部变量,该变量只初始化一次,并且在下一次调用使用的是上一次被改变过的值。修饰普通函数和全局变量,函数和全局变量不能被其他模块用extern关键字引入,只能在本模块中使用。修饰成员变量或者成员函数,该成员变量或者成员函数可以通过类名加::来访问,也可以通过实例访问。在static修饰的成员函数中不能直接访问非静态成员变量,因为无法指定该成员变量是属于某个实例的成员变量。而static修饰的成员变量属于类而不专门属于某个实例,而是各个实例共享的变量。被static修饰的成员函数声明在类内,但是实现在类外。
  26. 虚函数实际的函数放在什么地方?代码段中。
  27. 如何理解多态:多态,意思是一个接口,会产生多种调用。一个接口,多种形态。c++中有静态多态,和动态多态。静态多态,主要有普通函数重载、类的成员函数的重载,函数模板三种。而动态多态,是指父类中有一个虚函数被子类所重写,定义了一个父类指针,这个指针可能指向父类实例,可能指向子类实例,这要等到代码具体执行的时候才能确定。指向父类实例,调用该方法会直接调用父类中的虚函数。而指向子类实例,调用该方法时会调用子类中重写的虚函数。这就是动态多态。静态多态是在编译时确定所调用函数的入口地址。
  28. 静态类型和动态类型:静态类型:变量被声明时的类型,编译时确定。动态类型,变量实际上所指向的类型,运行时确定。如:Base *b=new Derive()。Base是静态类型,Derive是动态类型。
  29. c++中,如果父类中的方法没有声明为虚函数,而子类中定义了一个同名同参同返回值的函数,这不是覆盖,这是隐藏。所以c++中如果重写一个方法,一定是父类中的函数定义为虚函数。
  30. 验证子类中重写的父类的虚函数,该重写函数也是虚函数:定义个子类的指针指向空,用这个指针去调用重写函数,无法调用。非虚函数的地址是在编译时确定的,运行时可以通过类的指针会找到这个地址,进行调用。说明该函数也是虚函数。
  31. c++中如何写一个函数运行于主函数之前?写一个普通函数,写一个类,在类的构造方法中去调用这个普通函数。声明一个全局的类实例。在声明这个实例的过程中就会调用类的构造函数,从而在构造函数中去调用你想要调用的那个方法了。
  32. c++中非0就是true,0就是false
  33. 单例模式的c++实现:懒汉式
    class Singlelazy{
    private:
          Singlelazy(){}
          static Singlelazy* instance;
    public:
          static Singlelazy* get_instance(){
                std::cout<<"get instance"<<std::endl;
                if(instance==nullptr){
                      instance=new Singlelazy();
                      std::cout<<"is null"<<std::endl;
                }
                return instance;
          }
          static void destory(){
                delete instance;//如果静态实例不在类外进行初始化本处编译不能通过。
          }
    };
    Singlelazy* Singlelazy::instance=nullptr;

     

  34. 单例模式c++饿汉式:
    class Single{
    private:
          Single(){}
          static Single* instance;
    public:
          static Single* get_instance(){
                return instance;
          }
          static void destory(){
                delete instance;
          }
    };
    Single* Single::instance =new Single();

     

  35. c++中类的实例化:Base b;或者是Base *b=new Base()。Base b这种方式,可以用于饿汉式单例模式,但是不能用于懒汉式单例模式。因为Base b这样就已经将b实例分配了内存。如果加static修饰就是在全局区分配内存。所以c++中的单例模式,无论懒汉饿汉,一般都是Base *b来提供实例。
  36. 饿汉式单例模式是线程安全的,因为对象只被实例化一次。而懒汉式单例模式,判断当前类中提供的实例是否为空,如果为空,则进行实例化,有可能多个线程通过了实例为空的判断,进行实例化。因为懒汉式单例模式不是线程安全的。可以通过加锁的方式来使懒汉式单例模式变得安全。
  37. 虚函数和纯虚函数:只有在类内才能定义一个虚成员函数。不能将普通函数定义为虚函数或者纯虚函数。纯虚函数是没有函数体的。虚函数表中,指向纯虚函数的指针值为0,而非纯虚函数则是这个函数的入口地址。在c++中只要一个类含有纯虚函数,这个类就是抽象类,不能被实例化。抽象类中可以含有其他非纯虚函数,但只要含有一个纯虚函数,就是抽象类。子类继承父类,一定要重写父类中的纯虚函数,不然子类也是纯虚函数,不能被实例化。
  38. c++中的switch语句中的形参,整形,字符型,布尔型,枚举型,都可以。
  39. 工厂模式,分为三种,简单工厂模式,工厂只有一个,一个工厂生产多种产品。工厂方法模式,工厂和产品都有多个,一个工厂对应一种产品。抽象工厂模式,工厂有多个,产品有多种系列,每个系列有多种型号,一个工厂对应一系列的产品。总结:https://blog.csdn.net/jigetage/article/details/79605624
  40. 观察者模式:当对象间存在一对多的关系时,某个对象被改变,自动通知所有依赖于这个对象的对象,并自动更新。
  41. 装饰器模式:给对象增加新的功能而不必改变他的结构。装饰器模式比通过创建新的子类来增加功能更加方便。
  42. 设计模式之MVC模式:模型-视图-控制器模式:模型是一个存储数据的对象,视图负责将模型中的数据进行可视化,控制器负责让数据流向模型,并且在数据变化时更新视图。
  43. 指针是否为空可以作为if语句的判断条件。
  44. 清空一个vector中的元素用clear()方法,但是并没有清空这个vector的内存,清空内存用的是swap(),用一个空的vector来替换现有的vector
  45. c++中在全局变量中定义一个整型数组,数组中的元素会被默认设置为0,在栈中定义一个数组,数组中的元素则不一定全部为0,可能是任意的值。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值