• C++重要知识点小结---2


    C++重要知识点小结--1 :http://www.cnblogs.com/heyonggang/p/3246631.html

    1.C++允许程序员声明一个不能有实例对象的类,这样的类惟一的用途是被继承。这种类成为抽象类。

    一个抽象类至少具有一个纯虚函数。所谓纯虚函数是指被标明为不具体实现的虚成员函数。

    如:

    virtual void WithDrawal(float amount) = 0;  //纯虚函数

    在WithDrawal()的声明之后的“=0”表明程序员将不定义该函数。该声明是为派生类而保留的位置

    一个抽象类不能有实例对象,即不能由该类抽象来制造一个对象。

    纯虚函数是在基类中为子类保留的一个位置,以便子类用自己的实在函数定义来重载之。如果在基类中没有保留位置,则就没有重载。

    纯虚函数是一个没有定义函数语句的基类虚函数,纯虚函数的值是0.派生类必须为每一个基类纯虚函数提供一个相应的函数定义。

    2.派生类可以继承基类的所有公有保护的数据成员和成员函数。

    保护的访问权限对于派生类来说是公有的,而对于其它的对象来说是私有的。即使是派生类也不能访问基类的私有的数据成员和成员函数

    在派生类中允许重载基类的成员函数。如果基类中的函数是虚函数,当使用指针或引用访问对象时,将基于实际运行时指针所指向的对象类型来调用派生类的函数。

    3.笔试,面试中常考的C++虚拟继承的知识点

    第一种情况:         第二种情况:          第三种情况            第四种情况:
    class a           class a              class a              class a
    {              {                {                 {
        virtual void func();      virtual void func();       virtual void func();        virtual void func();
    };              };                  char x;              char x;
    class b:public virtual a   class b :public a           };                };
    {              {                class b:public virtual a      class b:public a
        virtual void foo();        virtual void foo();     {                 {
    };              };                  virtual void foo();        virtual void foo();
                                   };                };

    如果对这四种情况分别求sizeof(a),  sizeof(b)。结果是什么样的呢?下面是输出结果:(在vc6.0中运行)
    第一种:4,12
    第二种:4,4
    第三种:8,16
    第四种:8,8

    可参考:http://blog.csdn.net/wangqiulin123456/article/details/8059536

    想想这是为什么呢?

    因为每个存在虚函数的类都要有一个4字节的指针指向自己的虚函数表,所以每种情况的类a所占的字节数应该是没有什么问题 的,那么类b的字节数怎么算呢?看“第一种”和“第三种”情况采用的是虚继承,那么这时候就要有这样的一个指针vptr_b_a,这个指针叫虚类指针,也 是四个字节;还要包括类a的字节数,所以类b的字节数就求出来了。而“第二种”和“第四种”情况则不包括vptr_b_a这个指针,这回应该木有问题了 吧。

    1 class a
    2 {
    3     virtual void func();
    4 };
    5 
    6 class b:public a
    7 {
    8     void foo();
    9 };

    此时:sizeof(a) = 4 , sizeof(b) = 4

    1 class a
    2 {
    3    void func();
    4 };
    5 
    6 class b:public a
    7 {
    8     virtual void foo();
    9 };

    此时:sizeof(a) = 1 , sizeof(b) = 4

    1 class a
    2 {
    3    void func();
    4 };
    5 
    6 class b:public a
    7 {
    8     void foo();
    9 };

    此时:sizeof(a) = 1 , sizeof(b) = 1

    如下例:

     1 class A
     2 {
     3 };
     4 class A2
     5 {
     6 };
     7 class B : public A
     8 {
     9 };
    10 class C : public virtual B
    11 {
    12 };
    13 class D : public A , public A2
    14 {
    15 };
    View Code

    以上答案分别是1 , 1 , 4 , 1. 这说明:空类所占空间为1,单一继承的空类空间也为1,多重继承的空类空间还是1.但是虚继承涉及到虚表(虚指针),所以sizeof(C)的大小为4

    4.多继承的构造顺序

    构造对象的规则需要扩展以控制多重继承。构造函数按下列顺序被调用:

    1. 任何虚拟基类的构造函数按照它们被继承的顺序构造;
    2. 任何非虚拟基类的构造函数按照它们被继承的顺序构造;
    3. 任何成员对象的构造函数按照它们声明的顺序调用;
    4. 类自己的构造函数。

    5.C++子类继承父类后子类的大小

     1 #include <iostream>
     2 using namespace std;
     3 class  A 
     4 {
     5 private:
     6  int a;
     7 };
     8 
     9 class B:public  A
    10 {
    11 private:
    12  int b;
    13 };
    14 
    15 int main()
    16 {
    17  cout<<sizeof(A)<<endl;
    18  cout<<sizeof(B)<<endl;
    19  return 0;
    20 }

    刚开始我一想子类继承父类不会继承父类的私有变量,如此我认为结果为4,4(错误)。而事实上结果是4,8。也就是说子类把父类的私有变量也继承下来了,但是却无法访问,对于我这种菜鸟来说一下子没法转个弯来,后来看看资料焕然大悟,子类虽然无法直接访问父类的私有变量,但是子类继承的父类的函数却可以访问,不然的话如果只继承函数而不继承变量,哪么父类的函数岂不成了无米之炊了。所以必须把父类的所有变量都继承下来,这样既能保护父类的变量也能使用父类的函数。

    6.继承的访问控制

    继承分为公共继承保护继承私有继承

    在公共继承的类中,基类的每个成员在子类中保持同样的访问方式。即在基类中为public的成员,子类中可以访问,并据为public的;基类中为protected的成员,子类中也可访问之,并据为protected的;基类中为private的成员,在子类中不能访问之,这就像在应用程序中不能访问类中似有成员一样。

    访问控制权限:

    • 私有继承时,基类中不管是公有的,还是保护的或者为私有的,一律在子类中变成私有成员。
    • 保护继承时,基类中公共和保护的成员在子类中变成保护的,而基类中私有的成员在子类中变成私有的。
    • 公共继承时,基类中为公共、保护和私有的成员在子类中仍保持为公共、保护和私有的。

    如果不标明继承为公共还是保护或者私有,则默认的继承是私有的

    在继承关系中,基类的private成员不但对应用程序隐藏,甚至对派生类也隐藏。而基类的保护成员则只对应用程序隐藏,而对派生类则毫不隐瞒。

    一个私有的或保护的派生类不是子类,因为非公共的派生类不能做基类能做的所有的事。

    保护继承与私有继承类似,继承之后的类相对于基类来说是独立的;保护继承的类对象,在公开场合同样不能使用基类的成员。

    派生类的构造函数必须激活所有基类的构造函数,并把相应的参数传递给它们。

    也可参看:http://www.cnblogs.com/heyonggang/archive/2013/04/17/3026107.html

    7.虚函数

    C++虚函数用于实现动态绑定,或者说多态,默认的类方法是非虚函数,需要动态绑定的类方法,必需显式声明函数 virtual。

    virtual函数必需在子类中再次声明,明确告诉子类有这个方法,否则编译时报错,getRange方法未声明的错误。

    复制代码
    #include <iostream>
    using namespace std;
    class Range {
    public:
        int width;
        int height;
        virtual float getRange();
        
        Range(int w, int h):width(w), height(h){};
        Range(){};
    };
    
    float Range::getRange() {
        return width * height;
    }
    
    class Square:public Range {
    public:
        virtual float getRange();
        Square(){};
        Square(int w, int h):Range(w, h){};
    };
    float Square::getRange() {
        return width * width * 2;
    }
    
    class Circle:public Range {
    public:
        virtual float getRange();
        Circle(){};
        Circle(int w, int h):Range(w, h){};
    };
    float Circle::getRange() {
        return 3.14 * width * width / 2;
    }
    int main(int argc, char* args[]) {
        Square s1(3, 4);
        Circle c1(2, 5);
        Range *r1 = &s1;
        cout << r1->getRange() << endl;
        Range *r2 = &c1;
        cout << r2->getRange() << endl;
        return 0;
    }
    复制代码

    输出结果为:

    18

    6.28

    Square 和 Circle 都由一个 Range 指针指向,当调用 getRange方法,动态找到相应 Square 和 Circle 实例的getRange方法进行调用。

    纯虚函数

    C++的纯虚函数用于表示一个类不能被创建实例, 必需是子类覆盖该方法的定义后,方可新建类实例,格式是在虚函数后面添加 = 0。

    假如上例中的Range只是一个初步表示区域的一个类,那么它的getRange()方法需要由子类实现才有效,表示为:

    复制代码
    virtual float getRange() = 0;
    复制代码

    此时不能再创建Range rt()实例,将会报错:

    cannot declare variable ‘rt’ to be of abstract type ‘Range’
    range2.cpp:3:13: note: because the following virtual functions are pure within ‘Range’:

    但我们仍然可以新建Range的指针,指向Circle或者是Square

    一个有意思的问题:为什么析构函数要设置成虚函数

    Range *r1 = new Circle(3, 4);

    如果析构函数不是虚函数,则r1在释放内存时,则调用提Range的析构函数。

    结果并不是想要的结果,我们想要的结果是调到Circle对象的析构函数。

    如果析构函数是虚函数,有多态的支持,r1调用Circle对象的析构函数,Circle对象的析构函数默认调用父类Range的析构函数,保证Circle和Range对象的内容都得到清除。

  • 相关阅读:
    关于springboot项目使用yml类型的配置文件
    关于个人电脑连不上公司svn服务器,显示拒绝访问的错误
    改变思考问题的方式——SQL排序查询
    FreeMarker入门级
    个人电脑安装svn实录
    tomcat配置虚拟路径,可以解决实际开发中测试时前端访问后台电脑上的图片的问题
    springmvc的运行原理个人见解
    [CF915F] Imbalance Value of a Tree
    [CF768G] The Winds of Winter
    [BZOJ4241] 历史研究
  • 原文地址:https://www.cnblogs.com/heyonggang/p/3253036.html
Copyright © 2020-2023  润新知