将成员变量声明为private

为什么成员变量不该是public?

(1)从语法一致性来说,如果成员变量不是public,就需要通过成员函数访问成员变量。public接口内的每样东西都是函数的话,客户就不需要在访问class成员时考虑要不要加小括号。(2)如果成员变量是public,任何人都可以读写它,而设为private后,可以更加精确地控制它,可以实现“不准访问”、“只读访问”以及读写访问。(3)封装,比如class中有一个成员变量mean,可以用成员函数来访问它(也就是封装了它),这样你可以替换不同的实现方式,客户最多只需重新编译。封装非常重要,你对客户隐藏成员变量(也就是封装了它们),你可以确保class的约束条件总是会获得维护,因为只有成员函数可以影响到它们。如果不隐藏,即使有class的源码,改变public事物还是会受到束缚,因为会破坏很多客户代码。public意味着不封装,不封装就意味着不可改变。protected并不比public更具封装性

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

想象有一个class表示网页浏览器,其中有几个函数用来清除缓存、历史记录和cookies:

class WebBrowser {
public:
...
void clearCache();//清除缓存
void clearHistory();//清除历史记录
void removeCookies();//清除cookies
...
};

许多用户想一键清除缓存、历史记录和cookies,因此再提供一个函数:

//方法1 增加一个成员函数
class WebBrowser {
public:
...
void clearEverything();//调用clearCache、clearHistory和removeCookies
...
}; //方法2 增加一个非成员函数
void clearBrowser(WebBrowser& wb){
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}

那么哪种方法更好呢?方法2 非成员函数的方法更好。为什么呢

(1)非成员函数的封装性更高。如果一些东西被封装,它就不再可见。越多东西被封装,越少人可以看见它;越少人看见它,就有越大的弹性改变它。对于数据来说,越少的代码可以访问它,封装性就越高。当成员变量为private时,只有成员函数和友元函数可以访问它。方法1增加的成员函数clearEverything()不止可以访问class内的private函数,还可以取用private函数、enums、typedefs等等;而方法2的非成员函数,无法访问上述的东西,两者机能相当,因此选择并不增加“能够访问class内之private成分”的函数数量。

(2)方便机能扩充。在c++中,比较自然的做法是作为非成员函数并位于一个namespace中:

//头文件“webbrowser.h” 针对class WebBrowser自身
namespace WebBrowserStuff{
class WebBrowser{...}
...//核心机能,所有客户都需要的,非成员函数
}
// 头文件 “webbrowserbookmarks.h”
namespace WebBrowserStuff{
... //与书签相关的函数
}
// 头文件 “webbrowsercookies.h”
namespace WebBrowserStuff{
... //与cookies相关的函数
}

比如客户要再加影音下载相关的函数,只要写个头文件内含其声明即可

值得注意的是,因为在意封装性我们选择了非成员函数,但这并不意味着它“不可以是别的class的成员函数”,可以写一个工具类,clearBrowser()作为工具类的static member函数。

若所有参数皆需类型转换,请为此采用非成员函数

假设有一个类表示有理数:

class Rational {
public:
Rational(int numerator = 0, int denominator = 1) :n(numerator), d(denominator) {};
int numerator() const { return n; }//分子的访问函数
int denominator() const { return d; }//分母的访问函数
const Rational operator*(const Rational& rhs) const;
private:
int n, d;
};
const Rational Rational::operator*(const Rational& rhs) const
{
return Rational(this->n * rhs.n, this->d * rhs.d);
}

这样的设计可以让有理数相乘:

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

但是当你进行混合算数时:

Rational result = oneEight * 2; //可以
Rational result = 2 * oneEight; //错误

由于整数2没有相应的operator*函数所以报错。而oneEight * 2发生了隐式类型转换,将2转变为Rational。这是因为构造函数为non-explicit的,编译器才会自动转换,如果构造函数是explicit的,上两句都报错。

如果你想让他支持混合式算术运算,就让operator*成为非成员函数:

const Rational operator*(const Rational& r1, const Rational& r2)
{
return Rational(r1.numerator() * r2.numerator(), r1.denominator() * r2.denominator());
}

无论何时如果可以避免friend函数就该避免,因为就像真实世界一样,朋友带来的麻烦旺旺多过价值。

【Effective C++】设计与声明——成员变量和成员函数的更多相关文章

  1. 2.2 C++类的成员变量和成员函数

    参考:http://www.weixueyuan.net/view/6334.html 总结: 类成员的声明和定义: 类成员函数的定义分类内定义(内联)和类外定义(可用 inline 关键字 强制转换 ...

  2. 【转】C++ const成员变量和成员函数(常成员函数)

    转:http://c.biancheng.net/view/2230.html 在类中,如果你不希望某些数据被修改,可以使用const关键字加以限定.const 可以用来修饰成员变量和成员函数. co ...

  3. C++ const成员变量和成员函数(常成员函数)

    在类中,如果你不希望某些数据被修改,可以使用const关键字加以限定.const 可以用来修饰成员变量和成员函数. const成员变量 const 成员变量的用法和普通 const 变量的用法相似,只 ...

  4. java面向对象---成员变量和成员函数

    //成员变量 1.类定义了对象中所具有的变量,这些变量称作成员变量 2.每个对象都有自己的变量,和同一个类的其他对象的分开的 //函数与成员变量 1.在函数中可以直接写成员变量的名字来访问成员变量,那 ...

  5. c/c++ 类成员变量,成员函数的存储方式,以及this指针在c++中的作用

    c/c++ 类成员变量,成员函数的存储方式,以及this指针在c++中的作用 c++不会像上图那样为每一个对象的成员变量和成员函数开辟内存空间, 而是像下图那样,只为每一个对象的成员变量开辟空间.成员 ...

  6. Java的初始化执行顺序(父类static变量->子类static变量->父类成员变量->父类构造器->成员变量->构造器->main函数)

    1. 引言 了解Java初始化的顺序,有助于理解Java的初始化机制和内存机制. 顺序:父类static变量->子类static变量->父类成员变量->父类构造器->成员变量- ...

  7. 成员变量和成员函数前加static的作用?

    成员变量和成员函数前加static的作用?答:它们被称为常成员变量和常成员函数,又称为类成员变量和类成员函数.分别用来反映类的状态.比如类成员变量可以用来统计类实例的数量,类成员函数负责这种统计的动作 ...

  8. Effective C++ ——设计与声明

    条款18:让接口更容易的被使用,不易误用 接口设计主要是给应用接口的人使用的,他们可能不是接口的设计者,这样作为接口的设计者就要对接口的定义更加易懂,让使用者不宜发生误用,例如对于一个时间类: cla ...

  9. Effective C++ —— 设计与声明(四)

    条款18 : 让接口容易被正确使用,不易被误用 欲开发一个“容易被正确使用,不容易被误用”的接口,首先必须考虑客户可能做出什么样的错误操作.  1. 明智而审慎地导入新类型对预防“接口被误用”有神奇疗 ...

  10. [原]Unity3D深入浅出 - 常用类的成员变量和成员函数(Tranform、Time、Random、Mathf、Input)

    Transform的成员变量 Transform的成员函数 Time类,获取和时间相关的信息,可用来计算帧速率,调整时间流逝的速度等. Random类,可用来生成随机数,随机点和旋转. Mathf类提 ...

随机推荐

  1. 10个常用的JS工具库,80%的项目都在用

    高手区别于普通人的重要一点是,他们善于利用工具,把更多的时间留给了规划和思考.写代码也是同样的道理,工具用好了,你就有更多的时间来规划架构和攻克难点.今天就给大家分享一下当前最流行的 js 工具库,如 ...

  2. 《Effective C#》系列之(六)——提高多线程的性能

    一.综述 <Effective C#>中提高多线程性能的方法主要有以下几点: 避免锁竞争:锁的使用会导致线程阻塞,从而影响程序的性能.为了避免锁竞争,可以采用无锁编程技术,如CAS(Com ...

  3. 6980. 【2021.02.03冬令营模拟】你的世界(world) Another Solution

    Problem Description Input 从文件 world.in 中读入数据. Output 输出到文件 world.out 中. 输出共 T 行,第 i 行表示第 i 组测试数据的答案, ...

  4. 剑指offer29(Java)-顺时针打印矩阵(简单)

    题目: 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字. 示例 1: 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]输出:[1,2,3,6,9,8,7,4,5 ...

  5. 力扣907(java)-子数组的最小值之和(中等)

    题目: 给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组. 由于答案可能很大,因此 返回答案模 10^9 + 7 . 示例 1: 输入:arr = ...

  6. 力扣92(java&python)-反转链表Ⅱ(中等)

    题目: 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right .请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 . ...

  7. Android Native crash 处理案例分享

    简介: Android Native crash 处理案例分享 1. 背景 目前 mPaas[1] Android使用Crash SDK对闪退进行的处理,CrashSDK 是 Android 平台上一 ...

  8. 一文详解SQL关联子查询

    简介: 本文主要介绍什么是关联子查询以及如何将关联子查询改写为普通语义的sql查询. 本文主要介绍什么是关联子查询以及如何将关联子查询改写为普通语义的sql查询. 在背景介绍中我们将讲讲常见的关联子查 ...

  9. 零基础玩转SLS日志查询-SLS Query Builder发布

    ​简介:日志服务(Log Service,简称 SLS) 是阿里云提供的行业领先的日志大数据解决方案,一站式提供数据收集.清洗.分析.可视化.告警等功能.智能查询分析是数据中台重要的一环,SLS支持秒 ...

  10. [Go] 有了 cast 组件, golang 类型转换从此不再困扰

    在 golang 中,参数和返回值之间往往涉及 int.string.[].map 等之间的转换. 如果是手动去处理,一容易出错,二不能兼容多数类型,比较麻烦. 使用 cast,能够让代码更健壮.可维 ...