《C++编程惯用法——高级程序员常用方法和技巧》——2.3 公用数据-阿里云开发者社区

开发者社区> 异步社区> 正文

《C++编程惯用法——高级程序员常用方法和技巧》——2.3 公用数据

简介:
+关注继续查看

本节书摘来自异步社区出版社《C++编程惯用法——高级程序员常用方法和技巧》一书中的第2章,第2.3节,作者: 【美】Robert B. Murray ,更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.3 公用数据

假设有如下一个复数类:

class Complex {
public:
   double real;
   double imag;
   Complex (double r, double i) : real (r), imag(i) {}
   //其他的被忽略
};

这个类可能可以工作,但它的接口却有着重大的缺陷。问题出在那两个公用的数据成员:real和imag上面。这个接口保证Complex对象中的实部和虚部会以浮点数的形式存储在对象中。由于用户代码可以直接存取到公用的数据成员:

Complex c(3.0,4.0);
double d = c.real;
c.imag = 0.0;

所以一旦我们需要改变信息的存储格式或存储位置时,这样的改动会变得相当困难。

假设在后面的某个版本中,我们希望将Complex的实现(而不是接口)中的信息存储方式由笛卡儿坐标格式改为极坐标格式。(也许我们有着的某些算法在处理使用极坐标格式表示的数字时效果最好。)如果笛卡儿坐标是公用数据,我们就将陷入泥潭:我们将不得不改变所有的用户代码或者是在每个Complex对象中同时维持极坐标和笛卡儿坐标格式。

如果我们在一开始就避免使用公用数据,我们就不会碰到这样的问题了:

class Complex {
private:
   double real_d;
   double imag_d;
public:
   Complex(double r,double i):real_d(r),imag_d(i){}
   double real() const { return real_d; }
   duoble imag() const { return imag_d; }
   void real(double r) { real_d = r; }
   void imag(double i) { imag_d = i; }
 
   //此处忽略细节
};

一旦接口中指定可以通过Complex对象得到浮点型的实部和虚部之后,如何在对象中存储这些浮点数就变成了类的私有实现细节。用户代码要想取得这些数据,就必须调用类的成员函数,而不是直接去访问数据成员。

Complex c(3.0, 4.0);
double d = c.real();
c.imag (0.0);

现在对于用户来说,唯一的不同就是他们为了获取值将不得不多输入两个字符(即括号)。由于Complex::real是一个内嵌函数,所以这两种形式产生的代码应该是一致的——将取值操作封装在一个内嵌函数中不会带来任何效率上的损失。同样,我们也可以将修改值的操作封装到内嵌函数中去。

现在,我们的类中再也没有公用数据了,为了将信息以其他格式存储而修改类的实现也变得容易起来:

//文件Complex.h
class Complex {
private:
   double r;
   double theta;
public:
   Complex(double re, double im); //不再是内嵌函数了
   double real() const { return r*sin(theta); }
   double imag() const { return r*cos(theta); }
   void real(double); //不再是内嵌函数了
   void imag(double); //不再是内嵌函数了

   //此处忽略细节
};

//文件Complex.c
Complex::Complex(double re, double im)
   : r(sqrt( re*re + im*im )),
    theta (atan2 (im,re)){
}

  //"real(double)"和"imag(double)"留给读者作为练习

此时,用户代码必须重新编译,程序的性能也会有一定的影响,但程序的接口没有改变。原来可以正常工作的程序仍然可以正常工作。

我们也可以将实现改为把信息存储到其他的位置上去:

class Complex_rep {
private:
   friend class Complex;
   double real_d;
   double imag_d;
   Complex_rep(double r, double i): real_d(r),imag(i){}
};

class Complex {
private:
   Complex_rep*rep;
public:
   Complex(double r, double i)
   : rep(new Complex_rep(r,i)){}
   ~Complex() { delete rep;}
   //此处忽略细节
};

我们将在第3章中解释为什么这么做。

2.3.1 表示不变量(Representation invariant)

公用数据还会使得我们的类难以用来保证表示不变量。表示不变量是一个谓词,对于某个完全被构建好的对象,它都将得到真值。

例如,假设我们有一个用来表示有理数的类Rational,它里面有两个整型数(一个用于分子,一个用于分母):

class Rational {
private:
   int num_d;
   int denom_d,
public:
   Rational(int n, int d);
   int num()const { return num_d;}
   int denom()const { return denom_d;}
   voie num(int n) {num_d = n;}
   void denom(int);//改变分母
};

如果我们的类具有这么一个表示不变量(分母denom_d不可能为0),这将会使得我们的算法变得更简单,并且更高效。我们在每个可能改变分母的函数中都对新的分母进行检测,如果它是0的话,我们就向外报告一个错误[2]:

static void
check_zero(int d){
   if (d == 0) {
     cerr << "Zero denominator in Rational\n";
     abort();
   }
}

Rational::Rational(int n, int d)
: num_d(n),
  denom_d(d) {
   check_zero(d);
)

void
Rational::denom(int d){
   check_zero(d);
   denom_d = d;
}

有了这两个函数我们就可以保证不变量的成立,因此在Rational的其他成员函数中,我们并不需要担心出现分母为0的情况。如果我们将分母声明为一个公用数据成员,我们就无法来保证不变量的成立,用户代码也就可以在任意时刻将分母赋值为0。有关Rational的所有操作也都将不得不对这种可能性进行检测。

综上所述,我们得到如下的结论:避免公用数据成员的出现。公用成员将使得我们很难去更改类的实现(如改变信息的存储格式或者存储位置);它也无法保证表示不变量的长期有效性。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
算法学习之路|用C++刷算法会用到的STL(二)——set
用C++刷算法会用到的STL(二)——set
2378 0
黑马程序员 五、异常处理和常用类)
Java帮帮-IT资源分享网  五、黑马程序员—异常处理和常用类 第五篇  1、异常 异常:就是不正常,是指程序在运行时出现的不正常情况。其实就是程序中出现的问题。 这个问题按照面向对象思想进行描述,并封装成了对象。因为问题的产生有产生的原因、有 问题的名称、有问题的描述等多个属性信息存在。当出现多属性信息最方便的方式就是将这 些信息进行封装。异常
1443 0
11.Linux shell编程(常用命令)
(创建于2018/1/31) 文件操作 1.显示列表 ls -l 以列表的形式显示 ls -la 以列表的形式显示所有,包括隐藏文件 ls -l ja* 通配符查找,查找的如果是文件夹结果是以列表形式显示文件夹下第一层的所有文件和文件夹 ls -l...
797 0
每个程序员都必须知道的8种通用数据结构
数据结构是一种特殊的组织和存储数据的方式,可以使我们可以更高效地对存储的数据执行操作。数据结构在计算机科学和软件工程领域具有广泛而多样的用途。
638 0
SAS学习笔记之《SAS编程与数据挖掘商业案例》(4)DATA步循环与控制、常用全程语句、输出控制
SAS学习笔记之《SAS编程与数据挖掘商业案例》(4)DATA步循环与控制、常用全程语句、输出控制 1. 各种循环与控制 DO组 创建一个执行语句块 DO循环 根据下标变量重复执行DO和END之间的语句 DO WHILE 重复执行直到条件为假则退出循环 DO UNTIL 重复执行直到条件为真则退出循环 DO OVER 对隐含下标
1240 0
+关注
异步社区
异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
11941
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载