ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【EffectC++】22、23、24

2021-06-15 18:57:36  阅读:232  来源: 互联网

标签:24 non 函数 22 void Effect member Rational class


Item22:将成员变量声明为private

成员变量应该是private的

为什么不采用public成员变量?

  1. 一致性。所有的成员变量都提供访问接口,一个类对外提供的形式就只有函数形式
  2. 使用函数可以让你对成员变量处理有更加精确的控制。可读、可写、读写等
  3. 封装性。当我们有了这个类更好的实现时,而且成员变量都是private的,我们就可以在不破坏客户代码的前提下更换为更好的实现;成员变量在被读写时轻松的通知其他对象;可以验证class的约束条件;可以在多线程中提供同步控制(加锁)等

某种东西的封装性与“当其内容改变时可能造成的代码破坏量”呈反比

protected变量的封装性是不是要比public的要好呢?答案是不一定。当public成员变量被修改,所有使用了它的客户代码破坏;当protected成员变量被修改,所有使用了它的derived class代码被破坏,而后进一步延伸

成员变量只有两种权限:private(封装)和非private(不封装)

总结:

  • 切记将成员变量声明为private。这可以提供客户访问数据的一致性、可细微划分访问控制、允许约束条件获得保证、提供class作者以充分的弹性实现
  • protected并不比public更具有封装性

Item23:宁以non-member、non-friend函数替换member函数

//一个class用来表示网页浏览器 有的函数清理缓存 有的清理记录 有的清理cookie
class WebBrowser{
public:
    ...
    void clearCache();
    void clearHistory();
    void removeCookie();
};

//很多用户想要一整个执行上述操作 两种方法member、non-member
class WebBrowser{
public:
    ...
    void clearCache();
    void clearHistory();
    void removeCookie();
    void clearEverything();
};

void clearEverything(WebBrowser &wb){
    wb.clearCache();
    wb.clearHistory();
    wb.removeCookie();
}
//哪种方法好呢?

non-member、non-friend函数更好,member函数带来的封装性要比non-member、non-friend函数低:

  1. 封装性:愈少的人看到他我们就有愈大的弹性去变化它,这是我们推崇封装性的原因,它使我们能够改变事物而只影响有限客户。考虑对象内的数据,越多的函数能够访问到它,说明它的封装性越差。增加non-member、non-friend函数不会降低对象内数据的封装性!!

    1. 这个论述只适用于non-member且non-friend函数 member和friend函数对class的private成员访问的权力是一样的
    2. non-member、non-friend函数可以变成另一个类的member函数
  2. 编译相依

    C++的做法是让class WebBrowser和non-member函数位于同一个namespace中

    namespace WebBrowserStuff{
    class WebBrowser{
    public:
        ...
        void clearCache();
        void clearHistory();
        void removeCookie();
    };
    
    void clearEverything(WebBrowser &wb){
        wb.clearCache();
        wb.clearHistory();
        wb.removeCookie();
    }
    }
    

    namespace和class不同,namespace可以跨文件而class不能

    我们的namespace std不只有一个头文件<C++ standard library>,而是一共有几十个头文件,因为我们想用到vector就不需要queue等库,这样降低了编译相依

    一个WebBrowser这样的class可能有大量的便利函数clearEverything,有些与书签相关、有些与cookie相关等,一个只与书签相关的便利函数不需要与cookie相关的便利函数发生编译相依。分离他们的直接做法就是放在不同的头文件中

    //头文件webbrowser.h
    namespace WebBrowser{
    class WebBrowser{
    public:
        ...
        void clearCache();
        void clearHistory();
        void removeCookie();
    };	
    }
    //头文件webbrowserbookmarks.h 书签相关的便利函数
    namespace WebBrowser{
    
    }
    //头文件webbrowsercookies.h cookies相关的便利函数
    namespace WebBrowser{
    
    }
    //把不同的non-member便利函数放在不同的头文件中可以降低编译相依
    
  3. 机能扩充性

    这种方法切割不能用于class中的member函数,因为class是一个整体,不能被切割放在不同的头文件

    将所有便利函数放在同一个命名空间中,意味着客户可以轻松扩展这一组便利函数,他们要做的就是在同一命名空间下建一个不同的头文件。比如WebBrowser中放一个下载相关的便利函数,只需要在WebBrowser的命名空间中新建一个下载相关的头文件。这样做对于class是不行的,因为class对于客户是不能修改的!!

总结:

  • 宁可拿non-member non-friend函数代替member函数。这样做可以增加封装性、包裹弹性和机能扩充性

Item24:若所有参数皆需要类型转换,请为此采用non-member函数

当我们想要设计一个class表示有理数, 显然ints转化为这个有理数的隐性转换是合理的,所以构造函数是non-explicit的

//一个表现有理数的class
class Rational{
private:
    int n, d;//n分子 d分母
public:
    Rational(int numerator = 0, int denominator = 1) : n(numerator), d(denominator){}
    int n() const;
    int d() const;
};

现在我们想要实现operator*的方法,应该是member函数还是non-member呢?首先我们假定为member函数

//一个表现有理数的class
class Rational{
private:
    int n, d;//n分子 d分母
public:
    Rational(int numerator = 0, int denominator = 1) : n(numerator), d(denominator){}
    int n() const;
    int d() const;
    const Rational operator*(const Rational &rhs) const;
};
Rational oneEight(1, 8);
Rational oneHalf(1, 2);
Rational result = oneEight * oneHalf;
result = result * oneEight;//正确

result = oneHalf * 2;//正确
result = 2 * oneHalf;//错误

上述语句等同于

result = oneHalf.operator*(2);//正确
//实际执行
//const Rational temp(2);	//隐式转换
//result = oneHalf * temp;
result = 2.operator*(oneHalf);//错误

结论:只有当参数被列于参数列中,这个参数才是隐式转换类型的合格参与者 ,被调用的成员函数所隶属的那个对象(this对象)绝不是隐式类型转换的合格参与者

所以我们应该用non-member函数来写operator*

//一个表现有理数的class
class Rational{
private:
    int n, d;//n分子 d分母
public:
    Rational(int numerator = 0, int denominator = 1) : n(numerator), d(denominator){}
    int n() const;
    int d() const;
};

const Rational operator*(const Rational&lhs, const Rational &rhs);

Rational oneEight(1, 8);
Rational oneHalf(1, 2);
Rational result = oneEight * oneHalf;
result = result * oneEight;//正确

result = oneHalf * 2;//正确
result = 2 * oneHalf;//正确

那么这个operator*需要是friend函数吗?本例不需要,因为它用的是Rational的public接口

所以一定记住member对立面是non-member,而不是freind,不要想着某个与类相关的函数不是member的,就应该是freind的!!!无论何时如果你能避免friend函数就应该避免,因为现实中朋友带来的便利往往少于带来的麻烦

当从objectivec++迈向templatec++时,这个条款就不一定适用了

总结:

  • 如果你需要为某个函数的所有参数(包括this指向的隐喻参数)进行类型转换,那么这个函数必须是non-member的

标签:24,non,函数,22,void,Effect,member,Rational,class
来源: https://blog.csdn.net/moX980/article/details/117930958

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有