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

一.相关知识点 

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

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

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

二.相关代码

1.

#include 
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 
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 
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 
using namespace std;

/*nestfrnd.cpp*/
#include 
#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 = "<6.

#include 
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 
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 
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 
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 
#include 

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 
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 
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<


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

#include "handle.h"
#include 
#include 

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;
}


上述答案仅供参考,如有错误希望大家指出,谢谢大家~

你可能感兴趣的:(原书代码,C++,题解,C语言)