您的位置:首页 > 编程语言 > C语言/C++

《C++编程思想》第三章 隐藏实现 (原书代码+习题+解答)

2015-07-27 15:45 399 查看
一.相关知识点

1. 在C++中,存取控制并不是面向对象的特征,但它为类的创建者提供了很有价值的访问控制。类的用户可以清楚地看到,什么可以用,什么应该忽略。更重要的是,它保证了类的用户不会依赖任何类的实现细节。有了这些,我们就能更改类的实现部分,没有人会因此而受到影响,因为他们并不能访问类的这一部分。一旦我们有了更改实现部分的自由,就可以在以后的时间里改进我们的设计,而且允许犯错误。要知道,无论我们如何小心地计划和设计,都可能犯错误。知道犯些错误也是相对安全的,这意味着我们会变得更有经验,会学得更快,就会更早完成项目。一个类的公共接口部分是用户能看到的。所以在分析设计阶段,保证接口的正确性更加重要。但这并不是说接口不能作修改。如果我们第一次没有正确地设计接口部分,我们可以再增加函数,这样就不需要删除那些已使用该类的程序代码。

2.可见的实现部分

有些项目不可让最终用户看到其实现部分。例如可能在一个库的头文件中显示一些策略信息,但公司不想让这些信息被竞争对手获得。比如从事一个安全性很重要的系统 (如加密算法),我们不想在文件中暴露任何线索,以防有人破译我们的代码。或许我们把库放在了一个“有敌意”的环境中,在那里程序员会不顾一切地用指针和类型转换存取我们的私有成员。在所有这些情况下,就有必要把一个编译好的实际结构放在实现文件中,而不是让其暴露在头文件中。

3.句柄类( handle classes)

C++中的存取控制允许将实现与接口部分分开,但实现的隐藏是不完全的。编译器必须知道一个对象的所有部分的声明,以便创建和管理它。我们可以想象一种只需声明一个对象的公共接口部分的编程语言,而将私有的实现部分隐藏起来。但 C + +在编译期间要尽可能多地做静态类型检查。这意味着尽早捕获错误,也意味着程序具有更高的效率。然而这对私有的实现部分来说带来两个影响:一是即使程序员不能轻易地访问实现部分,但他可以看到它;二是造成一些不必要的重复编译。

二.相关代码

1.

#include <iostream>
using namespace std;
/*public.cpp*/
struct A
{
int i;
char j;
float f;
void foo();
};

void A::foo()
{}

struct B
{
public:
int i;
char j;
float f;
void foo();
};

void B::foo()
{}

int main()
{
return 0;
}


2.

#include <iostream>
using namespace std;

/*private.cpp*/

struct B
{
private:
char j;
float f;
public:
int i;
void foo();
};

void B::foo()
{
i = 0;
j = '0';
f = 0.0;
}

int main()
{
B b;
b.i = 1;//OK public
//b.j = '1';Illegal,private
//b.f = 1.0;Illegal,private

return 0;
}


3.保护(protected)

最后一种存取指定符是 protected。 protected与private基本相似,只有一点不同:继承的结构可以访问protected成员,但不能访问private成员

4.

#include <iostream>
using namespace std;

/*friend.cpp*/
struct X;

struct Y         //struct Y必须在它的成员Y :: f(X*)被声明为struct X的
//一个友元之前声明 ,但Y :: f(X*)要被声明, struct X又必须先声明
{
void f(X*);
};

struct X
{
private:
int i;
public:
void initialize();
friend void g(X*, int);
friend void Y::f(X*);
friend struct Z;//friend struct Z是一个不完全的类型说明,并把整个 struct都当作一个友元。
friend void h();
};

void X::initialize()
{
i = 0;
}

void g(X* x, int i)
{
x->i = i;
}

void Y::f(X* x)
{
x->i = 47;
}

struct Z
{
private:
int j;
public:
void initialize();
void g(X* x);
};

void Z::initialize()
{
j = 99;
}

void Z::g(X* x)
{
x->i += j;
}

void h()
{
X x;
x.i = 100;
}

int main()
{
X x;
Z z;
z.g(&x);

return 0;
}


5.

#include <iostream>
using namespace std;

/*nestfrnd.cpp*/
#include <string.h>
#define SZ 20

/*struct holder包含一个整型数组和一个 p ointer,我们可以通过 pointer来存取这些整数。因为
pointer与holder紧密相连,所以有必要将它作为 struct中的一个成员。一旦 pointer被定义,它就
可以通过下面的声明来获得存取 holder的私有成员的权限:
friend holder : :pointer ;
注意,这里struct关键字并不是必须的,因为编译器已经知道 pointer是什么了*/

struct holder
{
private:
int a[SZ];
public:
void initialize();
struct pointer
{
private:
holder* h;
int* p;
public:
void initialize(holder* H);
void next();
void previous();
void top();
void end();
int read();
void set(int i);
};
friend holder::pointer;
};

void holder::initialize()
{
memset(a,0,SZ*sizeof(int));
}

void holder::pointer::initialize(holder* H)
{
h = H;
p = h->a;
}

void holder::pointer::next()
{
if(p < &(h->a[SZ-1]))
{
p++;
}
}

void holder::pointer::previous()
{
if(p > &(h->a[0]))
{
p--;
}
}

void holder::pointer::top()
{
p = &(h->a[0]);
}

void holder::pointer::end()
{
p = &(h->a[SZ-1]);
}

int holder::pointer::read()
{
return *p;
}

void holder::pointer::set(int i)
{
*p = i;
}

int main()
{
holder h;
holder::pointer hp,hp2;
int i;

h.initialize();
hp.initialize(&h);
hp2.initialize(&h);
for(i = 0;i < SZ;++i)
{
hp.set(i);
hp.next();
}
hp.top();
hp2.end();
for(i = 0;i < SZ;++i)
{
cout<<"hp = "<<hp.read()<<","
<<"hp2 = "<<hp2.read()<<endl;

hp.next();
hp2.previous();
}

return 0;
}


6.

#include <iostream>
using namespace std;

/*class.cpp*/

struct A
{
private:
int i,j,k;
public:
int f();
void g();
};

int A::f()
{
return i + j + k;
}

void A::g()
{
i = j = k = 0;
}

class B//然而class在C + +中的使用逐渐变成了一个非必要的关键字。它和 struct
//的每个方面都是一样的,除了class中的成员缺省为私有的,而 struct中的
//成员缺省为public
{
int i,j,k;
public:
int f();
void g();
};

int B::f()
{
return i + j + k;
}

void B::g()
{
i = j = k = 0;
}

class X//许多人喜欢用一种更像struct的风格去创建一个类,
//因为可以通过以 public开头来重载 “缺省为私有”的类行为
{
public:
void interface_function();
private:
void private_function();
int internal_representation;
};

int main()
{
return 0;
}


7.

#include <iostream>
using namespace std;

/*stash.h*/

#ifndef STASH_H_
#define STASH_H_
class stash
{
int size;                 //Size of each space
int quantity;             //Number of storage spaces
int next;                 //Next empty space
unsigned char* storage;   //storage指针是一个unsigned char*。这是 C 编译器支持的最小的存储片,尽管在某些机器
//上它可能与最大的一般大,这依赖于具体实现。 storage指向的内存从堆中分配
void inflate(int increase);
/*inflate()函数使用realloc()为stash得到更大的空间块。 realloc()把已经分配而又希望重分配
的存储单元首地址作为它的第一个参数(如果这个参数为零,例如 initialize()刚刚被调用时,
realloc()分配一个新块)。第二个参数是这个块新的长度,如果这个长度比原来的小,这个块
将不需要作拷贝,简单地告诉堆管理器剩下的空间是空闲的。如果这个长度比原来的大,在堆
中没有足够的相临空间,所以要分配新块,并且要拷贝内存。 assert()检查以确信这个操作成
功。(如果这个堆用光了, malloc()、 calloc()和realloc()都返回零。)*/
public:
void initialize(int Size);//initialize()完成对 struct stash 的必要的设置,即设置内部变量为适当的值。最初,设置
//storage指针为零,设置size 指示器也为零,表示初始存储未被分配。
void cleanup();           //清除
int add(void* element);   //add()函数在stash的下一个可用位子上插入一个元素。首先,它检查是否有可用空间,如
//果没有,它就用后面介绍的 inflate() 函数扩展存储空间。
void* fetch(int index);   //fetch()首先看索引是否越界,如果没有越界,返回所希望的变量地址,地址的计算采用与
//add()中相同的方法
int count();              //返回所存储空间大小
};
#endif


8.

#include <iostream>
using namespace std;

/*stack.h*/
#ifndef STACK_H_
#define STACK_H_

class stack//这个嵌套 struct 称为 link,它包括指向这个表中的下一个 link 的指针和指向存放在 link 中的数据的指针,如果 next 指针是零,意味着表尾。
{
struct link
{
void* data;
link* next;
void initialize(void* Data, link* Next);
}*head;
public:
void initialize();
void push(void* Data);
void* peek();
void* pop();
void cleanup();//cleanup 去除每个栈元素,并释放data 指针
};
#endif


9.

#include <iostream>
using namespace std;

/*减少重复编译handle.h句柄类*/
#ifndef HANDLE_H_
#define HANDLE_H_

class handle
{
struct cheshire;//struct cheshire;是一个没有完全指定的类型说明或类声明(一个类的定义包含类的主体)
cheshire* smile;
public:
void initialize();
void cleanup();
int read();
void change(int);
};
#endif


#include "handle.h"
#include <stdlib.h>
#include <assert.h>

struct handle::cheshire//cheshire 是一个嵌套结构,所以它必须用范围分解符定义struct handle::cheshire {
//在handle::initialize()中,为cheshire struct分配存储空间在handle::cleanup()中这些空间被释放
{
int i;
}

void handle::initialize()
{
smile = (cheshire*)malloc(sizeof(cheshire));
assert(smile);
smile->i = 0;
}

void handle::cleanup()
{
free(smile);
}

int handle::read()
{
return smile->i;
}

void handle::change(int x)
{
smile->i = x;
}


#include "handle.h"
/*客户程序员唯一能存取的就是公共的接口部分,因此,只是修改了在实现中的部分,这些
文件就不须重新编译*/
int main()
{
handle u;
u.initialize();
u.read();
u.change(1);
u.cleanup();

return 0;
}


三.习题+解答

1) 创建一个类,具有public、 private 和protected数据成员和函数成员。创建该类的一个对象,看看当试图存取所有的类成员时会得到一些什么编译信息。

#include <iostream>
using namespace std;

class people
{
string phonenumber;
public:
int age;
string name;
string sex;
protected:
string address;
};

int main()
{
people p;
p.age = 19;
p.name = "John";
p.sex = "man";
//p.address = "ShangHai";     protected错误无法访问
//p.phonenumber = "123456789";private错误无法访问

return 0;
}


a.error C2248: 'address' : cannot access protected member declared in class 'people'
b.error C2248: 'phonenumber' : cannot access private member declared in class 'people'

2) 创建一个类和一个全局friend函数来处理类的私有数据。

#include <iostream>
using namespace std;

struct A
{
int i;
char c;
float f;
public:
void initialize();
void g();
friend void h();
};

void A::initialize()
{
i = 0;
c = '0';
f = 0.0;
}

void A::g()
{

cout<<i<<" "<<c<<" "<<f<<endl;
}

void h()
{
A a;
a.i = 1;
a.c = '1';
a.f = 1.0;
cout<<a.i<<" "<<a.c<<" "<<a.f<<endl;
}

int main()
{
A b;
b.initialize();
b.g();
h();

return 0;
}


3) 修改 HANDLE.CPP中的cheshire,重新编译和连接这一文件,但不重新编译USEHANDL.CPP。

#include "handle.h"
#include <stdlib.h>
#include <assert.h>

struct handle::cheshire//cheshire 是一个嵌套结构,所以它必须用范围分解符定义struct handle::cheshire {
//在handle::initialize()中,为cheshire struct分配存储空间在handle::cleanup()中这些空间被释放
{
int i;
int* p;
};

void handle::initialize()
{
smile = (cheshire*)malloc(sizeof(cheshire));
assert(smile);
smile->i = 1;
smile->p = NULL;
}

void handle::cleanup()
{
free(smile);
}

int handle::read()
{
return smile->i;
}

void handle::change(int x)
{
smile->i = x;
}


上述答案仅供参考,如有错误希望大家指出,谢谢大家~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: