(Boolan) C++面向对象高级编程(二)

2017-04-26  本文已影响52人  故事狗

之前写到了关于不带有指针的class的设计思路和注意事项,但是对于C/C++语言来说,还有一个非常重要的概念就是指针,为什么将指针作为class设计的分界点呢?那么,之前我讲了一个关于猫ヽ(=・ω・=)丿的故事,那么今天我再来讲一个关于国王和宝箱的故事吧 <( ‵□′)───C<─___-)||。

关于 指 针(钥匙)的一个小故事

语文老师教导,讲故事需要先说明时间地点人物发生了啥:
话说很久很久以前(发现词语好匮乏、、、),在遥远的国度有一个国王(突然想起了和尚),他有一件十分心爱的具备魔法的宝物,每次祭奠、仪式都会用到这个宝物的魔力。

而这个宝物由十个部件组成,每次只有将十个部件全部拼接在一起才会发挥他的魔力(少了任何一块都不行(¬_¬))。由于国王爱这个宝物爱的死去活来的,为了防止他过度沉溺在宝物的喜爱中无法自拔而耽误朝政((=。=)其实我想说,到底啥宝物这么有吸引力。。。故事设定。。。(▼皿▼#) ....就这样吧)。

国王的爹爹(居然没死╮(╯_╰)╭)将这个的碎片分别锁在了十个宝箱里面(ヾ(゚∀゚ゞ)这个故事背景我实在快编不下去了.......)。。。而每个箱子都对应一把独特的钥匙(废话。。。。。一把钥匙开一把锁━━( ̄ー ̄*|||━━),特殊之处在于
***这 个 钥 匙 不 但 可 以 打 开 箱 子 还 可 以 记 录 箱 子 在 哪 里 , ****。

而国王的爹爹把这十把钥匙分别交给了十个大臣来保管,也就是只有当国王召集了十个大臣以后才能够一睹宝物的真面目。

ps: (● ̄(エ) ̄●)背景就交代在这吧,虽然不合逻辑的地方很多,但是为了说清楚这个事,也就当作合理吧。

**** 那 么 问 题 来 了 !!!****( ⊙ o ⊙ )
等到国王的爹爹死了,国王也长大了不少,决心要做一个好的国王,已经不那么迷恋那个宝物了。但是每次在使用宝物的时候,国王发现实在太麻烦了,每次都要十个大臣全来了才能取出宝物的每个部件。有好几次急用的时候(,,#゚Д゚),大臣由于各种原因不能来把要是交给国王。还有的时候,十个大臣都来了,但是谁也不知道该开哪个箱子,每次都要一个个是试也是很大无时间的。
***有 没 有 办 法 能 帮 助 国 王 来 管 理 他 这 些 箱 子 呢 ( ▔•з•▔ )?****

这个 精彩绝伦(漏洞百出)的故事讲完了,但是这里面其实引出来的问题值得我们探讨一下的。方法太多了,我来简单说说几个拙见吧。

其实这个故事虽然存在很多不合理的地方,但实际是想做一个关于*** 数 据 结 构*** 的比喻。

C/C++毕竟是*** 直 接 面 对 内 存 ***的语言,所以程序员难免要考虑这些“箱子们和钥匙们” 的摆放和打开的问题###

***还 有 就 是 ,这 些 管 理 方 法 后 面 肯 定 是 有 用 的 ~ ~ ~ ~ ~ ~, 就 先 放 在 这 里 , 我 之 后 再 提 ***


回到之前的一个问题,为什么类要用带有指针和不带指针来进行区分呢???

那么假设你是国王,你希望自己的箱子被打不开或者被找不到吗?
对于带指针和不带指针的class最大的区别就是是否需要管理这些放在箱子里面的“宝藏”!!!如果管理不好,那么会出现丢失找不到的情况,但是宝藏其实就在那里只是我们再也找不到了,这时候,被丢失的宝藏就会出现一个问题,它会占用那些被我们找不到的空间。就像一个箱子的钥匙丢了,箱子里面全是好东西,但是我们拿不到,也砸不开那个箱子,但是他还在你家里面占了很多空间。。。。所以对于持有指针的class来说,手上拿到的指针,其实就相当于那把钥匙。管理钥匙的本质是为了管理箱子里的东西

class中带不带指针的问题关系着背后的数据(宝藏)该如何管理的问题

今天会用简化版本的string.h作为例子来说明带有指针的class应该如何设计,其实string相当于之前的方法三的管理方法,将所有的字符(char)在内存中有一排连续的空间(数组),将每个字母放在连续的空间中。就相当于国王的十个箱子里面分别一个字母组成[ h ][ e ][ l ][ l ][ o ][ w ][ o ][ r ][ l ][ d ]的一串字符串。这些“箱子”(内存单元)是连续的排列的方式组合在一起。这时候,我们只需要拿着[ h ]这个箱子的钥匙,就可以打开后面全部的箱子了,同时也可以根据每个箱子的位置来打开其中的箱子,取出其中的数据了(比如取出hello的[ o ],他位于这一排箱子的第五个位置,如果从0号开始排序,那么他就是相当于这串箱子的4号了,我们只需要拿着这个4号就能去走这个o了)。

后面该适当严肃点了,但还会呼应之前的故事,来解释 我想解释的内容 (´◊ω◊`) )

带有指针的class需要解决的Big Three问题

class String
{
    public:     
    //构造函数,接收一个字符串数组,通过字符串数组生成一个字符串(cstr相当于保管起来的那个钥匙)                              
       String(const char* cstr=0);
    //构造函数(拷贝构造,对于含有指针的class尤其要注意的)
    //接收参数为一个String本身,也就是通过接收String本身来构造一个新的String对象
       String(const String& str);       
    //操作符重载,(拷贝赋值,对于含有指针的class尤其要注意的)
    //操作符左右两侧均为String对象,也就是将一个String对象赋值给另外一个已有的String对象
       String& operator=(const String& str);
    //析构函数,对象死亡之前会默认调用的函数
    //防止指针失效了,但实际的数据还存在在内存中,无法拿到,也无法改变和删除
    //会造成内存泄漏,在对象死亡前需要再次管理那块内存空间
       ~String();                
    //获取私有属性,获取时没有改变内容,需要注意const的修饰(找钥匙的方法)                    
       char* get_c_str() const { return m_data; }
    private:
       char* m_data;  //相当于管理这一串宝箱的第一把钥匙
};
    inline
    String::String(const String& str)  //参数就是自己类型本身,可以使用引用,因为传入的数据不会被改动,需要使用const修饰
    {
        //因为是新创建的String对象,所以直接申请足够的内存空间,将内容拷贝过去即可
       m_data = new char[ strlen(str.m_data) + 1 ];
       strcpy(m_data, str.m_data);    //str.m_data:相同类型的对象互为友元,可以直接读取内部的private属性
    }

string.h的完整代码

    #ifndef __MYSTRING__
    #define __MYSTRING__

    class String
    {
    public:                                 
       String(const char* cstr=0);                     
       String(const String& str);                    
       String& operator=(const String& str);         
       ~String();                                    
       char* get_c_str() const { return m_data; }
    private:
       char* m_data;
    };

    #include <cstring>
    //通过字符串数组来构造一个字符串对象的构造函数
    inline
    String::String(const char* cstr)
    {
       //需要对传入的参数进行检查是否有内容
       if (cstr) {
          //需要针对传入的字符串数组的长度(strlen(cstr)来获得),但字符串后需要放置一个结束符/0,所以长度需要+1
          m_data = new char[strlen(cstr)+1];//在堆内存上,申请一块长度比传入字符数组长1的字符数组的内存空间
          strcpy(m_data, cstr);//使用strcpy函数将传入的参数cstr的所指的内存中的数据,赋值到新申请的内存空间上去,使两块内存空间都同时具备相同的数据
       }
       else {   //未指定初值的情况
       //虽然传入的部分为空,但是由于在C/C++中,管理字符串是以\0作为结尾的,即使是空字符串实际并不完全为空
       //所以在需要构造长度为一个单位的字符串数组,并在其中放入结束符/0
          m_data = new char[1];  //申请空间
          *m_data = '\0';       //保存结束符
       }
    }

    inline
    String::~String()
    {
       delete[] m_data;
    }

    inline
    String& String::operator=(const String& str)
    {
       if (this == &str)
          return *this;
           delete[] m_data;
       m_data = new char[ strlen(str.m_data) + 1 ];
       strcpy(m_data, str.m_data);
       return *this;
    }

    inline
    String::String(const String& str)
    {
       m_data = new char[ strlen(str.m_data) + 1 ];
       strcpy(m_data, str.m_data);
    }
      //输出,<<符号重载,不能为成员函数,因为操作符左侧为ostream
    #include <iostream>
    using namespace std;
        ostream& operator<<(ostream& os, const String& str)
    {
       os << str.get_c_str();
       return os;
    }
    #endif

内存管理初探


补充部分:

class Account{
public:
  static double m_rate;
  static void set_rate(const dounle& x){m_rate = x;};
};
double Account::m_rate = 8.0;  //对于静态数据必须在函数外进行定义,设置不设置初始值都可以

int main() 
{
  Account::set_rate(5.0);  //通过class name调用
  Account a;
  a.set_rate(7.0);  //通过object调用,对象的地址不会将a的地址作为this传入函数中
}
上一篇下一篇

猜你喜欢

热点阅读