《C++编程思想》第四章 初始化与清除(原书代码+习题+解答)

41 篇文章 2 订阅
15 篇文章 0 订阅

相关代码:

1.

#include <stdio.h>

class tree
{
	int height;
public:
	tree(int initialHeight);
	~tree();
	void grow(int years);
	void printsize();
};

tree::tree(int initialHeight)
{
	height = initialHeight;
}

tree::~tree()
{
	puts("inside tree destructor");
	printsize();
}

void tree::grow(int years)
{
	height += years;
}

void tree::printsize()
{
	printf("tree height is %d\n",height);
}

int main()
{
	puts("before opening brace");
	{
		tree t(12);
		puts("after tree creation");
		t.printsize();
		t.grow(4);
		puts("brfore closing brace");
	}
	puts("after closing brace");

	return 0;
}

2.

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

class G
{
	int i;
public:
	G(int I);
	void show();
};

G::G(int I)
{
	i = I;
}

/*void G::show()
{
	printf("%d\n",i);
}*/

int main()
{
#define SZ 100
	char buf[SZ];//我们可以看到首先是buf被定义,然后是一些语句,然后 x被定义并用一个函数调用对它初
                 //始化,然后y和g被定义
	printf("initialization value?");
	int retval = (int)gets(buf);
	assert(retval);
	int x = atoi(buf);
	int y = x+3;
	G g(y);
	//g.show();

	return 0;
}

3.

#include <iostream>
using namespace std;

class X
{
public:
	X()
	{}
};

void f(int i)
{
	if(i < 10)
	{
		//!goto jump1;//Error:goto bypasses init
	}
	X x1;
jump1:
	switch(i)
	{
	case 1:
		X x2;
		break;
	//!case 2://Error:case bypasses init
		X x3;//Constructor called here
		break;
	}
}
/*在上面的代码中, goto和switch都可能跳过构造函数的调用点,然而这个对象会在后面的
程序块中起作用,这样,构造函数就没有被调用,所以编译器给出了一条出错信息。这就确保
了对象在产生的同时被初始化。*/
int main()
{
	return 0;
}

4.

/*构造函数带多个参数*/
#include <iostream>
using namespace std;

class X
{
	int i,j;
public:
	X(int I, int J)
	{
		i = I;
		j = J;
	}
};

int main()
{
	X xx[] = {X(1,2),X(3,4),X(5,6),X(7,8)};
	/*注意,它看上去就像数组中的每个对象都对一个没有命名的构造函数调用了一次一样*/
	return 0;
}

5.

#ifndef NESTED_H_
#define NESTED_H_

class stack//这个嵌套 struct 称为 link,它包括指向这个表中的下一个 link 的指针和指向存放在 link 中的数据的指针,如果 next 指针是零,意味着表尾。
{
	struct link/*注意,虽然stack有构造函数与析构函数,但嵌套类 link并没有,这并不是说它不需要。当
                 它被使用时,问题就来了:*/
	{
		void* data;
		link* next;
		void initialize(void* Data, link* Next);
	}*head;
	stack();
	~stack();
	void push(void* Data);
	void* peek();
	void* pop();
};
#endif

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

void stack::link::initialize(void* Data, link* Next)
//简单地两次使用范围分解运算符,以指明这个嵌套 struct 的名字。 stack::link::initialize( ) 函数取参数并把参数赋给它的成员们
{
	data = Data;
	next = Next;
}

stack::stack()
{
	head = 0;
}

void stack::push(void* Data)
//stack::push( ) 取参数,也就是一个指向希望用这个 stack 保存的一块数据的指针,并且把
//这个指针放在栈顶
{
	link* newlink = (link*)malloc(sizeof(link));
	assert(newlink);
	newlink->initialize(Data, head);
	head = newlink;
}

void* stack::peek()
//返回head的值
{
	return head->data;
}

void* stack::pop()
//stack::pop( )取出当前在该栈顶部的 data 指针,然后向下移 head 指针,删除该栈老的栈顶元素
{
	if(head == 0)
	{
		return 0;
	}
	void* result = head->data;
	link* oldHead = head;
	head = head->next;
	free(oldHead);
	return result;
}

stack::~stack()
{
	link* cursor = head;
	while(head)
	{
		cursor = cursor->next;
		free(head->data);
		free(head);
		head = cursor;
	}
}

#include "nested.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>


int main(int argc, char** argv)
{
	assert(argc == 2);
	File* file = fopen(argv[1],"r");
	assert(file);
#define BUFSIZE 100
	char buf[BUFSIZE];
	stack textlines;
	while(fgets(buf, BUFSIZE, file))
	{
		char* string = (char*)malloc(strlen(buf)+1);
		assert(string);
		strcpy(string, buf);
		textlines.push(string);
	}
	char* s;
	while((s = (char*)textlines.pop()) != 0 )
	{
		printf("%s",s);
		free(s);
	}
	/*textlines的构造函数与析构函数都是自动调用的,所以类的用户只要把精力集中于怎样使
用这些对象上,而不需要担心它们是否已被正确地初始化和清除了*/

	return 0;
}


6.

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//C++
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:
	stash(int Size);          //构造函数
	~stash();                 //析构函数
	int add(void* element);   //add()函数在stash的下一个可用位子上插入一个元素。首先,它检查是否有可用空间,如
	                          //果没有,它就用后面介绍的 inflate() 函数扩展存储空间。
	void* fetch(int index);   //fetch()首先看索引是否越界,如果没有越界,返回所希望的变量地址,地址的计算采用与
	                          //add()中相同的方法
	int count();              //返回所存储空间大小
};

/*test.cpp*/

/*假设有一个程序设计工具,当创建时它的表现像一个数组,但它的长度能在运行时建
立。我称它为stash*/

#include "test.h"

stash::stash(int Size)
{
	size = Size; 
	quantity = 0;
	storage = 0; 
	next = 0;    
}

stash::~stash()
{
	if(storage)
	{
		puts("freeing storage");
		free(storage);
	}
}

int stash::add(void* element)
{
	if(next >= quantity)
	{
		inflate(100);
	}
	memcpy(&(storage[next * size]),element,size );
	/*我们必须用标准 C 库函数memcpy( )一个字节一个字节地拷贝这个变量,第一个参数是 memcpy()
	开始拷贝字节的目的地址,由下面表达式产生:
	                &(S->storage[S->next * S->size])
	它指示从存储块开始的第 next个可用单元结束。这个数实际上就是已经用过的单元号加一
	的计数,它必须乘上每个单元拥有的字节数,产生按字节计算的偏移量。这不产生地址,而是
	产生处于这个地址的字节,为了产生地址,必须使用地址操作符 &。
	memcpy()的第二和第三个参数分别是被拷贝变量的开始地址和要拷贝的字节数。 n e x t计数
	器加一,并返回被存值的索引。这样,程序员可以在后面调用 fetch( )时用它来取得这个元素。*/
	next ++;
	return (next - 1);
}

void* stash::fetch(int index)
{
	if(index >= next || index < 0)
	{
		return 0;
	}
	return &(storage[index * size]);
}

int stash::count()
{
	return next;
}

void stash::inflate( int increase)
{
	void* v = realloc(storage,(quantity + increase)*size );
	assert(v);
	storage = (unsigned char*)v;
	quantity += increase;
}

#include "test.h"
#define BUFSIZE 80

int main()
{
	stash intStash(sizeof(int));
	for(int i = 0;i < 100;++i)
	{
		intStash.add(&i);
	}
	FILE* file = fopen("main.cpp","r");
	assert(file);

	stash stringStash(sizeof(char)*BUFSIZE);
	char buf[BUFSIZE];
	while(fgets(buf, BUFSIZE, file))
	{
		stringStash.add(buf);
	}
	fclose(file);

	for(i = 0;i < intStash.count();++i)
	{
		printf("intStash.fetch(%d) = %d\n",i,
			*(int*)intStash.fetch(i));
	}

	for(i = 0;i < stringStash.count();++i)
	{
		printf("stringStash.fetch(%d) = %s",i,
			(char*)stringStash.fetch(i++));
	}
	putchar('\n');
	/*再看看cleanup()调用已被取消,但当 intStash和stringStash越出程序块的范围时,析构函数
被自动地调用了*/
	return 0;
}

习题+解答

1) 用构造函数与析构函数修改第3章结尾处的HANDLE.H,HANDLE.CPP 和USEHANDL.CPP文件。

#ifndef HANDLE_H_
#define HANDLE_H_

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

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

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

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

handle::~handle()
{
	free(smile);
}

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

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

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

	return 0;
}


2) 创建一个带非缺省构造函数和析构函数的类,这些函数都显示一些信息来表示它们的存在。写一段代码说明构造函数与析构函数何时被调用。

#include <iostream>
using namespace std;

class A
{
	int i;
	double d;
public:
	A(int I, double D);
	~A();
};

A::A(int I, double D)
{
	i = I;
	d = D;
	cout<<"Create! "<<i<<" "<<d<<endl;
}

A::~A()
{
	cout<<"Delete! "<<i<<" "<<d<<endl;
}

int main()
{
	A a(1,2.0);
	A b(3,4.0);
	A c(5,6.0);
	return 0;
}


3) 用上题中的类创建一个数组来说明自动计数与集合初始化。在这个类中增加一个显示信息的成员函数。计算数组的大小并逐个访问它们,调用新成员函数。

#include <iostream>
using namespace std;

class A
{
	int i;
	double d;
public:
	A();
	A(int I, double D);
	~A();
	void show();
};

A::A()
{
	i = 1;
	d = 1.0;
}

A::A(int I, double D)
{
	i = I;
	d = D;
	cout<<"Create! "<<i<<" "<<d<<endl;
}

A::~A()
{
	cout<<"Delete! "<<i<<" "<<d<<endl;
}

void A::show()
{
	cout<<i<<" "<<d<<endl;
}

int main()
{
	A aa[] = {A(1,2),A(3,4),A(5,6)};
	cout<<"sizeof(aa) = "<<sizeof(aa)<<endl;
	for(int i = 0;i < sizeof(aa)/sizeof(aa[0]);++i)
	{
		aa[i].show();
	}
	return 0;
}


4) 创建一个没有任何构造函数的类,显示我们可以用缺省的构造函数创建对象。现在为这个类创建一个非缺省的构造函数(带一个参数),试着再编译一次。解释发生的现象。

<span style="font-size:18px;">#include <iostream>
using namespace std;

class B
{
	int i;
};

int main()
{
	B b;
	return 0;
}</span>

<span style="font-size:18px;">#include <iostream>
using namespace std;

class B
{
	int i;
public:
	B(int I);
};

B::B(int I)
{
	i = I;
}

int main()
{
	B b(1);//B b;定义会报错,此时只调用定义的构造函数,不调用缺省构造函数
	return 0;
}</span>


以上代码仅供参考,如果有问题希望大家指出~谢谢大家


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值