• C++沉思录之一


    一、系统跟踪类Trace的设计过程:

    step1:简单版

    class Trace 
    {
    public:
        Trace()
        {
            noisy = 0;
        }
    
        void print(char* s)
        {
            if(noisy)
                print("%s", s);
        }
    
        void on()
        {
            noisy = 1;
        }
    
        void off()
        {
            noisy = 0;
        }
    
    
    private:
        int noisy;
    };

    step2:增加重定向功能,可能输出到别的文件中。

    class Trace 
    {
    public:
        Trace()
        {
            noisy = 0;
            f = stdout;
        }
    
        Trace(FILE *ff)
        {
            noisy = 0;
            f = ff;
        }
    
        void print(char* s)
        {
            if(noisy)
                fprintf(f, "%s", s);
        }
    
        void on()
        {
            noisy = 1;
        }
    
        void off()
        {
            noisy = 0;
        }
    
    private:
        int noisy;
        FILE *f;
    };

    步骤二的改动,基于这样一个事实:

      printf(args);

      等价于:

      fprintf(stdout, args);

    二、类设计者的核查表

      1. 你的类需要一个构造函数吗?

      足够复杂的类需要构造函数来隐藏它们的内部工作方式

      2. 你的数据成员是私有的吗?

      通常,使用公有的数据成员不是什么好事,因为类的设计者无法控制对这些数据成员的访问。

      3. 你的类需要一个无参构造函数吗?

      如果一个类有了构造函数,而你想声明该类对象的时候不必显示地初始化它们,则必须显式地写一个无参构造函数。

      例如:

    class Point
    {
    public:
        Point(int p, int q): x(p), y(q)
        {
    
        }
        //...
    
    private:
        int x, y;
    };
    
    
    Point p; //错误,没有合适的默认构造函数可用
    Point q[100];
    //错误,没有合适的默认构造函数可用

      4. 是不是每个构造函数初始化所有的数据成员?

      如果没有初始化所有的数据成员,很容易出现难以发现的错误。

      5. 你的类需要析构函数吗?

      注意:并不是所有存在构造函数的类都需要析构函数。

      需要不需要析构函数,主要在于该类是否分配了资源,而这些资源又不会伴随成员函数而自动释放。特别是那些在构造函数中有 new 操作符的类,通常要在析构函数中用 delete 来释放相对应的资源。

    •   6.你的类需要一个虚析构函数吗?
    •   注意:当一个类用于不会被继承时,这个类是不需要虚析构函数的。
    class Base
    {
    public:
        Base()
        {
            s = "";
        }
    
    private:
        string s;
    };
    
    
    class Dase : Base
    {
    public:
        Dase()
        {
    
        }
    private:
        string t;
    };
    
    int main()
    {
        Base *b = new Dase();
        delete b;//除非Base有一个虚析构函数,否则将调用错误的析构函数
    }

      7你的类需要一个复制构造函数吗?

      什么时候需要一个复制(拷贝)构造函数呢,正如在 何时需要自定义复制构造函数?里所说的,

      当类中的数据成员需要动态分配存储空间时,不可以依赖默认的复制构造函数。在需要时(包括这种对象要赋值、这种对象作为函数参数要传递、函数返回值为这种对象等情况),要考虑到自定义复制构造函数。另外,复制构造函数一经定义,赋值运算也按新定义的复制构造函数执行。

      下面,我们再举一个例子:

    class CString 
    {
    public:
        CString();
        CString(const char * s)
        {
    
        }
        //other function
    
    private:
        char *data;
    };

      我们可以知道,在构造函数中需要对数据成员 data 进行内存的动态分配,因此,类CString 需要一个析构函数,同理,它也需要一个显式的复制构造函数,如果没有的话,复制CString对象就会以复制它的 data 成员的形式隐式地定义,复制完成后,两个对象的 data 成员同时指向了同一个内存块,当这两个对象被销毁时,data 会被释放两次,这显然不是我们想要的。

      但如果不想用户能够复制CString 类的对象,可以定义复制构造函数(可能还有赋值操作符)为私有的。如下:

    class CString 
    {
    public:
        CString();
        CString(const char * s)
        {
    
        }
        //other function
    
    private:
        char *data;
    
        CString(const CString&);
        CString& operator= (const CString&);
    };

    实现如下:

    CString(const CString& s)
        {
            data = new char(strlen(s.data) + 1);
            strcpy(data, s.data);
        }

      8. 你的类需要一个赋值操作符吗?

       如果你的类需要复制构造函数,多半也需要赋值操作符,实现如下:

    CString& operator= (const CString& s)
        {
            if(&s != this)
            {
                delete [] data;
                data = new char(strlen(s.data) + 1);
                strcpy(data, s.data);
            }
            return *this;
        }

      9. 你的赋值操作符能正确地将对象赋值给对象本身吗?

      10. 你的类需要定义关系操作符吗?

       操作符的重载。

      11. 删除数组时你记住用 delete [] 了吗?

       内存的正确管理。

      12. 记得在复制构造函数和赋值操作符的参数类型中加上 const 了吗?

       有些C++的早期著作建议类 X 的复制构造函数的形式写成: X::X(X&)。这种建议是不正确的。

      复制构造函数的形式应该写成如下:

      X::X(const X&);     注意不同

      毕竟复制对象不会改变原对象的值。

      同理,这个也适合 重载操作符=。应该写成:

      X& operator= (const X&);

      13. 如果函数有引用参数,它们应该是const引用吗?

      14. 记得适当地声明函数为 const 的了吗?

  • 相关阅读:
    剖析Vue原理&实现双向绑定MVVM
    js处理异步的几种方式
    【转】从Vue.js源码看异步更新DOM策略及nextTick
    js中获取css样式属性值
    原生JS节点操作
    圣杯布局和双飞翼布局的作用和区别
    vue自定义插件-弹框
    MongoDB 数据库创建删除、表创建删除、数据增删改查
    Vue组件之间数据交互与通信
    Vue中的计算属性与$watch
  • 原文地址:https://www.cnblogs.com/wiessharling/p/3691977.html
Copyright © 2020-2023  润新知