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

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

相关代码:

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 = http://www.mamicode.com/Data;>
#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>


以上代码仅供參考。假设有问题希望大家指出~谢谢大家


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