zoukankan      html  css  js  c++  java
  • 继承与动态内存分配

    当继承和动态内存分配的问题交织在一起的时候,我们考虑类实现的时候,就需要考虑更多的东西,先上代码:

     1 #ifndef DMA_H
     2 #define DMA_H
     3 # include"iostream"
     4 using namespace std;
     5 class BaseDMA
     6 {
     7 //protected:    //将父类私有成员变量private声明为protected,则继承类也可直接访问成员变量,private表明除了自己的类,其余的都不可直接访问(就算是继承子类也不可以)
     8     //char* label;
     9     //int rating;
    10 private:
    11     char* label;
    12     int rating;
    13 public:
    14     BaseDMA(const char*l = "null", int r = 0);//构造函数
    15     BaseDMA(const BaseDMA & rs);//拷贝函数
    16     virtual ~BaseDMA();//虚析构函数,思考虚析构函数的意义,虚析构函数是为了避免多态时释放造成的内存泄露,因此,我们将基类的析构函数定义为虚函数是有好处的
    17     BaseDMA & operator=(const BaseDMA & rs);//赋值运算符重载,返回    BaseDMA & 是为了连续赋值,那么这又是为什么呢???
    18     friend ostream& operator<<(ostream& os, const BaseDMA & rs);//实际上,这里表明了ostream是一个类,返回ostream&是为了连续显示,友元函数并不是类成员函数
    19 };
    20 class LackDMA :public BaseDMA
    21 {
    22 
    23 private:
    24     enum{COL_LEN=40};//采用这样的定义类型更便于程序的管理
    25     char color[COL_LEN];
    26 public:    //思考要不要重新设置构造函数,析构函数,以及赋值拷贝函数,以及为什么不需要修改
    27     LackDMA(const char*c="black" , const char*l = "null", int r =0);
    28     LackDMA(const char*c , const BaseDMA & rs);
    29     LackDMA(const LackDMA & rs);
    30     friend ostream& operator<<(ostream& os, const LackDMA & rs);
    31 };
    32 
    33 class HasDMA :public BaseDMA
    34 {
    35 private:
    36     char *style;
    37 public:
    38     HasDMA(const char*s = "none", const char*l = "null", int r = 0);
    39     HasDMA(const char*s, const BaseDMA & rs);
    40     HasDMA(const HasDMA & rs);
    41     ~HasDMA();//为何上述lackdma代码段不需要重新定义析构函数,为何这里需要显示定义析构函数
    42     HasDMA & operator=(const HasDMA & rs);//为何这里的赋值拷贝函数也需要重新定义
    43     friend ostream& operator<<(ostream& os, const HasDMA & rs);
    44 };
    45 #endif

    上述类声明中,定义了一个基类BaseDMA,以及由该基类衍生的两个子类:LackDMA,HasDMA;其中,LackDMA类不涉及动态内存分配,HasDMA涉及动态内存分配。关于所涉及的其他知识细节,暂时不进行描述。下面给出类实现部分代码:

      1 #include"dma.h"
      2 # include "cstring"
      3 using namespace std;
      4 /////////BaseDMA类成员函数实现/////////////////////////
      5 BaseDMA::BaseDMA(const char*l = "null", int r = 0)
      6 {
      7     label = new char[strlen(l) + 1];
      8     strcpy(label, l);
      9     rating = r;
     10 }
     11 BaseDMA::BaseDMA(const BaseDMA & rs)
     12 {
     13     label = new char[strlen(rs.label) + 1];
     14     strcpy(label, rs.label);
     15     rating = rs.rating;
     16 }
     17 
     18 BaseDMA::~BaseDMA()//在虚构函数实现的时候,并不需要virtual关键字
     19 {
     20     delete[] label;
     21 }
     22 //我们发现下面的赋值函数和采用类引用的拷贝函数基本功能一致,思考其内在联系
     23 BaseDMA & BaseDMA:: operator=(const BaseDMA & rs)// 注意返回类型要放在作用域的前面
     24 {
     25     if (this == &rs)//思考在什么时候显示的使用this 指针
     26         return *this;
     27     delete[] label;//这里为何要使用delete[]删除原来
     28     label = new char[strlen(rs.label) + 1];
     29     strcpy(label, rs.label);
     30     rating = rs.rating;
     31     return *this;
     32 }   //仔细思考这一段代码背后的机制
     33 
     34 ostream& operator<<(ostream& os, const BaseDMA & rs)//需要注意的吧是。友元函数并不属于类的成员函数,因此这里并没有用BaseDMA::进行类作用域约束
     35 {
     36     os << "Label: " << rs.label << endl;
     37     os << "rating: " << rs.rating << endl;
     38     return os;//返回os是为了形成对<<a<<b的连续显示效果
     39 }
     40 ///////////////////////LackDMA类成员函数实现///////////////////////////
     41 LackDMA::LackDMA(const char*c = "black", const char*l = "null", int r = 0) :BaseDMA(l, r)
     42 {
     43     strncpy(color, c, 39);//注意这里的拷贝函数不再是strcpy而是strncpy;
     44     color[39] = '';//结束标志符
     45 }
     46 
     47 LackDMA::LackDMA(const char*c, const BaseDMA & rs) :BaseDMA(rs)
     48 {
     49     strncpy(color, c, 39);
     50     color[39] = '';
     51 }
     52 
     53 LackDMA::LackDMA(const LackDMA & rs) :BaseDMA(rs)
     54 {
     55     strncpy(color, rs.color, 39);
     56     color[39] = '';
     57 }
     58 
     59 ostream& operator<<(ostream& os, const LackDMA & rs)
     60 {
     61     //os << "Label: " << rs.label << endl;//遇到这种子类无法访问父类,该怎么办???除了可以使用将private声明为protected之外
     62     //os << "rating: " << rs.rating << endl;//我们当然可以使用将private成员声明成protected成员,使得子类获得访问权限。
     63     os << (const BaseDMA&)rs;//本质是通过基类的成员函数访问基类的成员变量,注意,子类是无法访问父类的私有成员变量的,必须通过父类的公有的成员函数进行访问。
     64     os << "color: " << rs.color << endl;
     65     return os;
     66 }
     67 ////////////hasDMA类成员函数实现/////////
     68 HasDMA::HasDMA(const char*s = "none", const char*l = "null", int r = 0) :BaseDMA(l, r)
     69 {
     70     style = new char[strlen(s) + 1];
     71     strcpy(style, s);
     72 }
     73 HasDMA::HasDMA(const char*s, const BaseDMA & rs) : BaseDMA(rs)
     74 {
     75     style = new char[strlen(s) + 1];
     76     strcpy(style, s);
     77 }
     78 HasDMA::HasDMA(const HasDMA & rs) : BaseDMA(rs)
     79 {
     80     style = new char[strlen(rs.style) + 1];
     81     strcpy(style,rs.style);
     82 }
     83 HasDMA::~HasDMA()
     84 {
     85     delete[] style;
     86 }
     87 HasDMA & HasDMA:: operator=(const HasDMA & rs)  //赋值运算符代码究竟该怎么写,写成:BaseDMA(rs)为什么是一种错误的写法
     88 {
     89     if (this == &rs)
     90         return *this;
     91     BaseDMA:: operator=(rs);//无论是哪种初始化方式,对基类的赋值是必不可少的。但这里为何要采用这种形式呢?
     92     delete[] style;
     93     style = new char[strlen(rs.style) + 1];
     94     strcpy(style, rs.style);
     95     return *this;//返回对象自身
     96 }
     97 ostream& operator<<(ostream& os, const HasDMA & rs)
     98 {
     99     os << (const BaseDMA &)rs;//本质上是通过基类的方法(基类的运算符重载)访问基类的成员变量,原理同LackDMA中描述的相同。
    100     os << "style:" << rs.style << endl;
    101     return os;
    102 }

    上述类成员实现的代码中,涉及了很多的知识细节,我们尤其要关注的是:

    1  子类不能直接访问父类的私有成员,必须通过父类的共有成员函数对其进行访问(如代码91行和99行,但这两处有所区别,思考其中的差异性),

    2   将private声明成protected,可以使得子类获得原私有成员的访问权限。(protected的作用就是为子类提供了一个访问权限,但对外仍然和private相同)

    3  友元函数并不是类成员函数,因此在函数实现的时候,并没有对其约定作用域解析符。

    4  对子类进行初始化的时候,一定是先对父类进行初始化。(无论是采用最原始的初始化,还是复制初始化,还是赋值初始化)

    5  思考92行的代码,为何进行delete[],那么何时进行的new呢???

    思考从声明子类到销毁子类,整个程序的执行过程???!!!

  • 相关阅读:
    java Object类源代码详解 及native (转自 http://blog.csdn.net/sjw890821sjw/article/details/8058843)
    使用库项目开发
    Android 开发规范
    使用Git之后出现android library引用失败
    Github安卓开源项目编译运行
    android项目引入三方类库配置文件
    Web交互设计优化的简易check list
    php程序效率优化的一些策略小结
    SQL语句优化原则
    纯PHP实现定时器任务(Timer)
  • 原文地址:https://www.cnblogs.com/shaonianpi/p/10375561.html
Copyright © 2011-2022 走看看