zoukankan      html  css  js  c++  java
  • 第55课 经典问题解析(四)

    关于动态内存分配:

    new Test共完成两步:

    1、申请堆空间

    2、在申请的堆空间上调用构造函数

    示例程序:

     第18行申请了内存空间,然后在申请的内存空间上调用了构造函数。

    而第19行仅仅是申请了内存空间。

    面向对象中尽量使用new。

    new是C++语言的一部分。

    delete:

     第25行的delete执行了析构函数,并且释放了内存空间。

    第26行仅仅释放了内存空间。

    new申请的内存要用delete释放,千万不要用free释放。因为用free释放时仅仅会释放内存,但是不会执行析构函数。

    delete一个对象时会先调用析构函数。

    malloc申请的内存,也不要用delete释放。有可能产生bug。

     关于虚函数:

     

    在构造函数执行结束之后,虚函数表指针才会被正确初始化,因此,执行构造函数时虚函数表指针还是随机值,所以,构造函数不能成为虚函数。

    析构函数在对象销毁之前被调用,因此,析构函数是可以成为虚函数的。

    实验:

     父类和子类的析构函数都没有定义成虚函数。delete指针p时只调用了父类的析构函数,没有调用子类的析构函数。

    当析构函数没有声明为虚函数时,编译器直接根据p的类型来决定调用哪个析构函数。

    将析构函数声明为虚函数,执行结果如下:

    这时编译器会根据p指针指向的实际类型来决定调用哪些析构函数,可以看到先调用子类的析构函数又调用父类的析构函数。

    在工程中将一个类作为父类时,习惯性的将析构函数声明为虚函数。否则有可能 产生内存泄漏,因为有可能子类的析构函数调用不到。

     

    在构造函数中调用虚函数或者在析构函数中调用虚函数,编译器怎么处理呢?

    在这两种情况下调用的虚函数必然是当前类中定义的版本。

     实验:

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 class Base
     7 {
     8 public:
     9     Base()
    10     {
    11         cout << "Base()" << endl;
    12         
    13         func();
    14     }
    15     
    16     virtual void func() 
    17     {
    18         cout << "Base::func()" << endl;
    19     }
    20     
    21     virtual ~Base()
    22     {
    23         func();
    24         
    25         cout << "~Base()" << endl;
    26     }
    27 };
    28 
    29 
    30 class Derived : public Base
    31 {
    32 public:
    33     Derived()
    34     {
    35         cout << "Derived()" << endl;
    36         
    37         func();
    38     }
    39     
    40     virtual void func()
    41     {
    42         cout << "Derived::func()" << endl;
    43     }
    44     
    45     ~Derived()
    46     {
    47         func();
    48         
    49         cout << "~Derived()" << endl;
    50     }
    51 };
    52 
    53 
    54 int main()
    55 {
    56     Base* p = new Derived();
    57     
    58     // ...
    59     
    60     delete p;
    61     
    62     return 0;
    63 }

    结果如下:

    由结果可以看出,13、23行的func函数都是调用的当前类中的版本。子类中的调用类似。

    关于继承中的强制类型转换:

    继承中如何正确的使用强制类型转换?

    示例:

    第29行的使用是错误的。

    因为编译器会检查dynamic_cast的使用是否合适。29行的使用是不合适的。

    因为dynamic_cast要求相关的类中必须有虚函数。

    我们无需另外定义一个虚函数,只需把析构函数声明为虚函数即可。

    再次编译就可以通过了。

    示例程序:

     可以看到转换不成功时返回0。

    dynamic_cast就是为C++中的面向对象程序设计而诞生的。

    小结:

  • 相关阅读:
    9月22日 又上锁妖塔
    1396. 【2014年鄞州区】挖掘机(d.pas/c/cpp)
    栓奶牛——二分解法
    P6188 [NOI Online 入门组]文具订购 题解
    HDC.Cloud | 基于IoT Studio自助生成10万行代码的奥秘
    华为云API Explorer开发者生态平台正式上线
    【华为云技术分享】揭秘华为云DLI背后的核心计算引擎
    【华为云技术分享】ARM体系结构基础(2)
    【华为云技术分享】HDC.Cloud | 以数字资产模型为核心驱动的一站式IoT数据分析实践
    【华为云技术分享】数据赋能,如何精细化保障企业大数据安全
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9588646.html
Copyright © 2011-2022 走看看