【Effective C++】设计与声明——成员变量和成员函数
将成员变量声明为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++】设计与声明——成员变量和成员函数的更多相关文章
- 2.2 C++类的成员变量和成员函数
参考:http://www.weixueyuan.net/view/6334.html 总结: 类成员的声明和定义: 类成员函数的定义分类内定义(内联)和类外定义(可用 inline 关键字 强制转换 ...
- 【转】C++ const成员变量和成员函数(常成员函数)
转:http://c.biancheng.net/view/2230.html 在类中,如果你不希望某些数据被修改,可以使用const关键字加以限定.const 可以用来修饰成员变量和成员函数. co ...
- C++ const成员变量和成员函数(常成员函数)
在类中,如果你不希望某些数据被修改,可以使用const关键字加以限定.const 可以用来修饰成员变量和成员函数. const成员变量 const 成员变量的用法和普通 const 变量的用法相似,只 ...
- java面向对象---成员变量和成员函数
//成员变量 1.类定义了对象中所具有的变量,这些变量称作成员变量 2.每个对象都有自己的变量,和同一个类的其他对象的分开的 //函数与成员变量 1.在函数中可以直接写成员变量的名字来访问成员变量,那 ...
- c/c++ 类成员变量,成员函数的存储方式,以及this指针在c++中的作用
c/c++ 类成员变量,成员函数的存储方式,以及this指针在c++中的作用 c++不会像上图那样为每一个对象的成员变量和成员函数开辟内存空间, 而是像下图那样,只为每一个对象的成员变量开辟空间.成员 ...
- Java的初始化执行顺序(父类static变量->子类static变量->父类成员变量->父类构造器->成员变量->构造器->main函数)
1. 引言 了解Java初始化的顺序,有助于理解Java的初始化机制和内存机制. 顺序:父类static变量->子类static变量->父类成员变量->父类构造器->成员变量- ...
- 成员变量和成员函数前加static的作用?
成员变量和成员函数前加static的作用?答:它们被称为常成员变量和常成员函数,又称为类成员变量和类成员函数.分别用来反映类的状态.比如类成员变量可以用来统计类实例的数量,类成员函数负责这种统计的动作 ...
- Effective C++ ——设计与声明
条款18:让接口更容易的被使用,不易误用 接口设计主要是给应用接口的人使用的,他们可能不是接口的设计者,这样作为接口的设计者就要对接口的定义更加易懂,让使用者不宜发生误用,例如对于一个时间类: cla ...
- Effective C++ —— 设计与声明(四)
条款18 : 让接口容易被正确使用,不易被误用 欲开发一个“容易被正确使用,不容易被误用”的接口,首先必须考虑客户可能做出什么样的错误操作. 1. 明智而审慎地导入新类型对预防“接口被误用”有神奇疗 ...
- [原]Unity3D深入浅出 - 常用类的成员变量和成员函数(Tranform、Time、Random、Mathf、Input)
Transform的成员变量 Transform的成员函数 Time类,获取和时间相关的信息,可用来计算帧速率,调整时间流逝的速度等. Random类,可用来生成随机数,随机点和旋转. Mathf类提 ...
随机推荐
- 重新整理数据结构与算法(c#)—— 二叉树排序树[二十二]
前言 什么是二叉堆排序呢? 就是上面这种,一个节点大于左节点,但是小于右节点,再我写的例子中会写出大于等于右节点. 那么如何让一个数组进行变成这种二叉树呢? 其实只要有规律就很简单. 第一个元素(0) ...
- three.js实现数字孪生3D仓库一期(开源)
大家好,本文使用three.js实现了3D仓库一期项目,给出了代码,分析了关键点,感谢大家~ 关键词:数字孪生.three.js.Web3D.WebGL.智慧仓库.开源 代码:Github 我正在承接 ...
- Docker部署Node应用简单实践
简介: 本文将从零至一,介绍如何在云服务器上通过 Docker 容器运行一个简单的Node应用. 前言 本文将从零至一,介绍如何在云服务器上通过 Docker 容器运行一个简单的Node应用.本文假设 ...
- Flutter+FaaS一体化任务编排的思考与设计
作者:闲鱼技术-古风 Flutter+Serverless三端一体研发架构,客户端不仅仅是编写双端的代码,而是扩展了客户端的工作边界,形成完整的业务闭环.在新的研发模式落地与实践的过程中,一直在思考如 ...
- 冷热分离之OTS表格存储实战
简介: 为什么要冷热分离由于2020疫情的原因,在线教育行业提前被大家所重视,钉钉教育已经服务超过21万所学校.700万教师和1.4亿学生用户,每天大量的教育数据产生.整体数据量:随着时间的积累,数据 ...
- 斩获大奖|阿里云PolarDB-X引领云原生分布式数据库新时代
简介:阿里云原生分布式数据库PolarDB-X荣获"2021年度最佳分布式数据库". 12月15-16日,以"引领分布式云变革 助力湾区数字经济"为主题的全球分 ...
- [ML] 工程师使用 Keras 的步骤指引
设置 import numpy as np import tensorflow as tf from tensorflow import keras 介绍 在训练模型之前准备数据(将其转换为 NumP ...
- AtCoder Beginner Contest 333
总结 人生第一次掉rating 各种降智操作 A 水题 B 逆天操作 WA了3发 第三次交的时候以为过了,等到切完E发现怎么B还没过( #include<bits/stdc++.h> us ...
- SAP Adobe Form 教程一 简单示例
马上需要用到adobe form,这里搬运一篇教程学习下. 英文原文:SAP Adobe Interactive Form Tutorial. Part I. First Adobe Form 本文链 ...
- docker 搭建LNMP环境
php7 仓库地址 https://gitee.com/haima1004/docker-lnmp