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++中的面向对象程序设计而诞生的。

    小结:

  • 相关阅读:
    Charles手机端抓包--证书
    新浪微盘批量下载
    [3140] Sublime Text 2.x, 3.x 通用注册码、密钥集合
    栈的数组实现
    栈的链表实现
    基数排序
    多项式相乘-数组实现
    最大子序列和问题的四种算法
    链表的实现
    时间同步算法与Simple Ring-based election algorithm算法分析-转载
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9588646.html
Copyright © 2011-2022 走看看