将成员变量声明为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. C++ 默认参数与引用传递:语法、用法及示例

    C++ 默认参数 默认参数概述 在 C++ 中,函数参数可以拥有默认值.这意味着,在调用函数时,如果省略了某个参数,那么将使用为该参数指定的默认值. 设置默认参数 默认参数值使用等号 = 符号进行设置 ...

  2. 有用的JavaScript技巧

    首次为变量赋值时务必使用var关键字 变量没有声明而直接赋值得话,默认会作为一个新的全局变量,要尽量避免使用全局变量. 使用===取代== ==和!=操作符会在需要的情况下自动转换数据类型.但===和 ...

  3. WIN11 安装 SQL Server 2019,SQLSERVER2022, MYSQL 8.0 ,Docker,Mongodb失败故障分析

    最近研究数据库性能调优遇到各种数据库各种装不上,不知道熬了多少根软白沙,熬了多少颗张三疯,问了多少AI,查了多少网页,熬了两天,终于搞明白了一件事:那就是WIN11 ON ARM (因为拿的是MAC ...

  4. 阿里云消息队列 Kafka-消息检索实践

    简介: 本文章主要介绍消息队列使用过程中所遇到的消息丢失.重复消费等痛点问题的排查办法,以及消息队列 Kafka「检索组件」的场景实践,并对其关键技术进行解读.旨在帮助大家对消息队列 Kafka「检索 ...

  5. Spring Boot参数校验以及分组校验的使用

    简介: 做web开发基本上每个接口都要对参数进行校验,如果参数比较少,还比较容易处理,一但参数比较多了的话代码中就会出现大量的if-else语句.虽然这种方式简单直接,但会大大降低开发效率和代码可读性 ...

  6. 基于MaxCompute+PAI的用户增长方案实践

    ​简介: 如何通过PAI+MaxCompute完成用户增长模型AARRR全链路,包含拉新.促活.留存.创收.分享. 本文作者 李博 阿里云智能 高级产品专家 在过去一年阿里云PAI机器学习团队做了很多 ...

  7. 用 SetWindowPos 方法设置一个停止响应的窗口将卡调用方

    我使用 User32 的 SetWindowPos 方法去设置一个跨进程的窗口,这个窗口是停止响应的,将让调用的 SetWindowPos 方法卡住,不继续执行逻辑.通过堆栈分析是卡在 NtUserS ...

  8. webpack调优技巧

    webpack优化主要有三个方面:1.提高构建速度,2.减少打包体积,3.优化用户体验 提高构建速度: 启用多线程 thread-loader 使用thread-loader插件可以启用多线程进行构建 ...

  9. Java工具类库大总结

    1. Java自带工具方法 1.1 List集合拼接成以逗号分隔的字符串 // 如何把list集合拼接成以逗号分隔的字符串 a,b,c List<String> list = Arrays ...

  10. 阿克曼函数(Ackermann function)部分推导

    相关题目 已知\(Ackermann function\)为 \[Ack(m,n)=\begin{cases}n+1(m=0) \\ Ack(m-1,1)(m>0,n=0) \\ Ack(m-1 ...