《C++编程思想》 第十二章 动态对象创建 (习题+解答)

一.相关知识点

重载new和delete

当创建一个new表达式时有两件事发生。首先,使用运算符new分配内存,然后调用构造函数。在delete表达式里,调用析构函数,然后使用运算符delete释放内存。我们永远无法控制构造函数和析构函数的调用(否则我们可能意外地搅乱它们),但可以改变内存分配函数运算符new和delete。

被new和delete使用的内存分配系统是为通用目的而设计的。但在特殊的情形下,它不能满足我们的需要。改变分配系统的原因是考虑效率:我们也许要创建和销毁一个特定的类的非常多的对象以至于这个运算变成了速度的瓶颈。 C++允许重载newdelete来实现我们自己的存储分配方案,所以可以像这样处理问题。

另外一个问题是堆碎片:分配不同大小的内存可能造成在堆上产生很多碎片,以至于很快用完内存。也就是内存可能还有,但由于是碎片,找不到足够大的内存满足我们的需要。通过为特定类创建我们自己的内存分配器,可以确保这种情况不会发生。

在嵌入和实时系统里,程序可能必须在有限的资源情况下运行很长时间。这样的系统也可能要求分配内存花费相同的时间且不允许出现堆内存耗尽或出现很多碎片的情况。由客户定制的内存分配器是一种解决办法,否则程序设计者在这种情况下要避免使用 new和delete,从而失去了C++很有价值的优点。

当重载运算符 new和delete时,记住只改变原有的内存分配方法是很重要的。编译器将用new代替缺省的版本去分配内存,然后为那个内存调用构造函数。所以,虽然编译器遇到 new时会分配内存并调用构造函数,但当我们重载 new时,可以改变的只是内存分配部分。( delete也有相似的限制。)

重载全局new和delete

当全局版本的new和delete不能满足整个系统时,对其重载是很极端的方法。如果重载全局版本,那么缺省版本就完全不能被访问—甚至在这个重载定义里也不能调用它们。

重载的new必须有一个size_t参数。这个参数由编译器产生并传递给我们,它是要分配内存的对象的长度。必须返回一个指向等于这个长度(或大于这个长度,如果我们有这样做的原因)的对象的指针,或如果没有找到存储单元(在这种情况下,构造函数不被调用),返回一个0。然而如果找不到存储单元,不能仅仅返回0,还应该调用new-handler或进行异常处理,通知这里存在问题。

运算符new的返回值是一个void*,而不是指向任何特定类型的指针。它所做的是分配内存,而不是完成一个对象的建立—直到构造函数调用了才完成对象的创建,这是由编译器所确保的动作,不在我们的控制范围内。

运算符delete接受一个指向由运算符new分配的内存的void *。它是一个void*因为它是在调用析构函数后得到的指针。析构函数从存储单元里移去对象。运算符 delete的返回类型是void。

对象放置

重载运算符new还有其他两个不常见的用途。

1) 可能想要在内存的指定位置上放置一个对象。这对于面向硬件的内嵌系统特别重要,在这个系统中,一个对象可能和一个特定的硬件是同义的。

2) 可能想在调用new时,可以从不同的内存分配器中选择。

这两种情形都用相同的机制解决:重载的运算符 new可以带多于一个的参数。像前面所看到的,第一个参数总是对象的长度,它是在内部计算出来的并由编译器传递。但其他参数可以

由我们自己定义:一个放置对象的地址、一个对内存分配函数或对象的引用、或其他方便的任何设置。起先在调用过程中传递额外的参数给运算符 new的方法看起来似乎有点古怪:在关键字

new后是参数表(没有size_t参数,它由编译器处理),参数表后面是正在创建的对象的类名字。

例如:

X* xp = new(a) X ;

将传递a作为第二个参数给运算符 new。当然,这是在这个运算符 new已经声明的情况下才有效的。

二.相关代码

1.

<span style="font-size:18px;"><strong>/*对使用 C的动态内存分配函数创建一个类*/
/*MALCLASS.cpp*/
#include <stdlib.h>//malloc() and free()
#include <string.h>//mesmet
#include "E:\VC++\7_31_2\allege.h"
#include <iostream.h>

class obj
{
	int i, j, k;
	enum{ sz = 100 };
	char buf[sz];
public:
	void initialize()
	{
		cout << "initialize obj" << endl;
		i = j = k = 0;
		memset(buf, 0 , sz);
	}
	void destroy()
	{
		cout << "destroying obj" << endl;
	}
};

int main()
{
	obj* Obj = (obj*)malloc(sizeof(obj));
	/*这里用户必须决定对象的长度(这也是程序出错原因之一)。因为它是一块内存而不是一
个对象,所以malloc()返回一个void*。 C++不允许将一个void* 赋予任何指针,所以必须映射。
因为malloc()可能找不到可分配的内存(在这种情况下它返回0),所以必须检查返回的指
针以确信内存分配成功。
但最坏的是:
Obj->initialize( ) ;
用户在使用对象之前必须记住对它初始化。*/
	allegemem(Obj);
	Obj->initialize();
	Obj->destroy();
	free(Obj);

	return 0;
}</strong></span>

2.

<span style="font-size:18px;"><strong>/*显示初始化发生的情况*/
/*NEWDEL.cpp*/
#include <iostream.h>
class tree
{
	int height;
public:
	tree(int Height)
	{
		height = Height;
	}
	~tree()
	{
		cout << "*";
	}
	friend ostream&
		operator << (ostream& os, const tree* t)
	{
		return os << "tree height is: "
			      << t->height << endl;
	}
};

int main()
{
	tree* T = new tree(40);
	cout << T;
	delete T;

	return 0;
}</strong></span>

3.

<span style="font-size:18px;"><strong>/*因为delete表达式对于不在堆上分配的指针是不安全的,
采取了一些步骤以防止在堆以外的地方创建 String*/
/*STRINGS.H*/

#ifndef STRINGS_H_
#define STRINGS_H_
#include <string.h>
#include <iostream.h>

class String
{
	char* s;
	String(const char* S)
		//为了限制用户使用这个类,主构造函数声明为 private,所以,除了我们以外,没有人可以使用它
	{
		s = new char[strlen(S) + 1];
		strcpy(s, S);
	}
	String(const String&);
	void operator=(String&);
	//另外,拷贝构造函数也声明为 private,但没有定义,以防止任何人使用它,运算符‘ =’也是如此
public:
	friend String* makeString(const char* S)
	{
		return new String(S);
	}
	static String* make(const char* S)
	{
		return new String(S);
	}
	//访问这个函数的方法有两种。为了使用简单,它可以是全局的friend函数(称为makeString())。
//但如果不想“污染”全局名字空间,可以使之为 static成员函数(称为 make())并通过
//String::make()调用它。后一种形式对于更加明显地表示它属于这个类是有好处的。
	~String()
	{
		delete s;
	}
	operator char*() const
	{
		return s;
	}
	char* str() const
	{
		return s;
	}
	friend ostream&
		operator<<(ostream& os, const String& S)
	{
		return os << S.s;
	}
};

#endif</strong></span>

4.

<span style="font-size:18px;"><strong>/*PSTASH.h*/
#ifndef PSTASH_H_
#define PSTASH_H_

class pstash
{
	int quantity;
	int next;
	void** storage;//storage是一个 void指针数组
	void inflate(int increase);
public:
	pstash()
	{
		quantity = 0;
		storage = 0;
		next = 0;
	}
	~pstash()
	{
		delete storage;
	}
	int add(void* element);
	void* operator[](int index) const;
	int count() const
	{
		return next;
	}
};

#endif</strong></span>

<span style="font-size:18px;"><strong>/*PSTASH.cpp*/
#include "pstash.h"
#include <iostream.h>
#include <string.h>

int pstash::add(void* element)
{
	const InflateSize = 10;
	if(next >= quantity)
	{
		inflate(InflateSize);
	}
	storage[next+1] = element;
	return (next-1);//index number
}

void* pstash::operator[](int index) const
{
	if(index >= next || index < 0)
	{
		return 0;
	}
	return storage[index];
}

void pstash::inflate(int increase)
{
	const psz = sizeof(void*);
	void** st = new void*[quantity + increase];
	memset(st, 0, (quantity + increase) * psz);
	memcpy(st, storage, quantity * psz);
	quantity += increase;
	delete storage;
	storage = st;
}
</strong></span>

<span style="font-size:18px;"><strong>/*PSTEST.cpp*/
#include "pstash.h"
#include "E:\VC++\8_4_1\strings.h"
#include <fstream.h>
#include "E:\VC++\7_31_2\allege.h"

int main()
{
	pstash intStash;
	for(int i = 0; i < 25; ++i)
	{
		intStash.add(new int(i));
		//伪构造函数形式,所以一个新的 int对象的内存将在堆上创建并且这个int对象被初始化为值i。
	}
	for(int u = 0; u < intStash.count(); ++u)
	{
		cout << "intStash[" << u << "] = "
			 << *(int*)intStash[u] << endl;
	}

	ifstream infile("pstest.cpp");
	allegefile(infile);
	const bufsize = 80;
	char buf[bufsize];
	pstash stringStash;
	for(int j = 0; j < 10; ++j)
	{
		if(infile.getline(buf, bufsize))
		{
			stringStash.add(makeString(buf));
		}
	}
	while(infile.getline(buf, bufsize))
	{
		stringStash.add(String::make(buf));
	}
	for(int v = 0; stringStash[v]; ++v)
	{
		char* p = *(String*)stringStash[v];
		//由运算符[]返回产生的指针必须被映射为 String*以使之具有正确的类型
		cout << "stringStash[" << v << "] = "
			 << p << endl;
	}

	return 0;
}
</strong></span>

5.

<span style="font-size:18px;"><strong>/*STACKl1.H*/
#ifndef STACKl1_H_
#define STACKl1_H_

class stack
{
	struct link
	{
		void* data;
		link* next;
		link(void* Data, link* Next)
		{
			data = Data;
			next = Next;
		}
	}*head;
public:
	stack()
	{
		head = 0;
	}
	~stack();
	void push(void* Data)
	{
		head = new link(Data, head);
	}
	void* peek() const
	{
		return head->data;
	}
	void* pop();
};

#endif
</strong></span>

<span style="font-size:18px;"><strong>/*STACKl1.cpp*/
#include "stackl1.h"
#include <stdlib.h>

void* stack::pop()
{
	if(head == 0)
	{
		return 0;
	}
	void* result = head->data;
	//使用void指针意味着在堆上创建的对象不能被 stack销毁
	link* oldHead = head;
	head = head->next;
	delete oldHead;
	return result;
}

stack::~stack()
{
	link* cursor = head;
	while(head)
	{
		cursor = cursor->next;
		delete head;
		head = cursor;
	}
}</strong></span>

<span style="font-size:18px;"><strong>/*STKTSTl1.cpp*/
#include "stackl1.h"
#include "E:\VC++\8_4_1\strings.h"
#include <fstream.h>
#include "E:\VC++\7_31_2\allege.h"
//这个程序没有删除 stack里的指针,stack本身也没有做,所以那块内存丢失了。
int main()
{
	ifstream file("stktstl1.cpp");
	allegefile(file);
	const bufsize = 100;
	char buf[bufsize];
	stack textlines;
	while(file.getline(buf, bufsize))
	{
		textlines.push(String::make(buf));
	}
	String* s;
	while((s = (String*)textlines.pop()) != 0)
	{
		cout << *s << endl;
	}
}</strong></span>

6.

<span style="font-size:18px;"><strong>/*NEWHANDL.cpp*/
#include <iostream.h>
#include <stdlib.h>
#include <new.h>
//new-handler 函数必须不带参数且具有 void返回值。 while循环将持续分配int对象(并丢掉
//它们的返回地址)直到全部内存被耗尽。在紧接下去对 new调用时,将没有内存可被分配,所
//以调用new-handler。
void out_of_memory()
{
	cerr << "memory exhausted!" << endl;
	exit(1);
}

int main()
{
	set_new_handler(out_of_memory);
	while(1)
	{
		new int[1000];
	}

	return 0;
}</strong></span>

7.

<span style="font-size:18px;"><strong>/*重载全局new和delete*/
/*GLOBLNEW.cpp*/
#include <stdio.h>
#include <stdlib.h>

void* operator new(size_t sz)
{
	printf("operator new: %d bytes\n", sz);
	//用printf()不会进入死锁状态,因为它不调用new来初始化本身。
	void* m = malloc(sz);
	if(!m)
	{
		puts("out of memory");
	}
	return m;
}

void operator delete(void* m)
{
	puts("operator delete");
	free(m);
}

class s
{
	int i[100];
public:
	s()
	{
		puts("s::s()");
	}
	~s()
	{
		puts("s::~s()");
	}
};

int main()
{
	puts("creating & destroying an int");
	int* p = new int(47);
	delete p;
	puts("creating & destroying an s");
	s* S = new s;
	delete S;
	puts("creating & destroying s[3]");
	s* SA = new s[3];
	delete []SA;

	return 0;
}</strong></span>

8.

<span style="font-size:18px;"><strong>/*为类 framis创建了一个非常简单的内存分配系统。程序开始时在静态数据区域内留出一块存储单元。
这块内存是用于 framis类型对象分配的内存空间。为了决定哪块存储单元已被使用,这里使用了一个
字节 ( bytes )数组,一个字节( byte )代表一块存储单元*/

/*FRAMIS.cpp*/
#include <stddef.h>
#include <fstream.h>
ofstream out("framis.out");

class framis
{
	char c[10];
	static unsigned char pool[];
	static unsigned char alloc_map[];
public:
	enum{ psize = 100 };
	framis()
	{
		out << "framis()\n";
	}
	~framis()
	{
		out << "~framis()...";
	}
	void* operator new(size_t);
	void operator delete(void*);
};

unsigned char framis::pool[psize * sizeof(framis)];
unsigned char framis::alloc_map[psize] = {0};

void* framis::operator new(size_t)
{
	for(int i = 0; i < psize; ++i)
	{
		if(!alloc_map[i])
		{
			out << "using block " << i << " ... ";
			alloc_map[i] = 1;
			return pool + (i * sizeof(framis));
		}
	}
	out << "out of memory" << endl;
	return 0;
}

void framis::operator delete(void* m)
{
	if(!m)
	{
		return;
	}
	unsigned long block = (unsigned long)m
		- (unsigned long)pool;
	block /= sizeof(framis);
	out << "freeing block " << block << endl;
	alloc_map[block] = 0;
}

int main()
{
	framis* f[framis::psize];
	for(int i = 0; i < framis::psize; ++i)
	{
		f[i] = new framis;
	}
	new framis;
	delete f[10];
	f[10] = 0;
	framis* x = new framis;
	delete x;
	for(int j = 0; j < framis::psize; ++j)
	{
		delete f[j];
	}

	return 0;
}
</strong></span>

9.

<span style="font-size:18px;"><strong>/*为数组重载new和delete
如果为一个类重载了运算符new和delete,那么无论何时创建这个类的一个对象都将调用这
些运算符。但如果为这些对象创建一个数组时,将调用全局运算符 new()立即为这个数组分配
足够的内存。全局运算符 delete()被调用来释放这块内存。可以通过为那个类重载数组版本的
运算符new[]和delete[]来控制对象数组的内存分配。*/
/*NEWARRY.cpp*/
/*但注意,申请的长度比期望的大 4个
字节。这额外的 4个字节是系统用来存放数组信息的,特别是数组中对象的数量。当用下面的
表达式时,方括号就告诉编译器它是一个对象数组,所以编译器产生寻找数组中对象的数量的
代码,然后多次调用析构函数。*/
#include <new.h>
#include <fstream.h>

ofstream trace("newarry.out");

class widget
{
	int i[10];
public:
	widget()
	{
		trace << "*";
	}
	~widget()
	{
		trace << "~";
	}
	void* operator new(size_t sz)
	{
		trace << "widget::new: "
			  << sz << "bytes" << endl;
		return ::new char[sz];
	}
	void operator delete(void* p)
	{
		trace << "widget::delete" << endl;
		::delete []p;
	}
	void* operator new[](size_t sz)
	{
		trace << "widget::new[]: "
			  << sz << " bytes" << endl;
		return ::new char[sz];
	}
	void operator delete[](void* p)
	{
		trace << "widget::delete[]" << endl;
		::delete []p;
	}
};

int main()
{
	trace << "new widget" << endl;
	widget* w = new widget;
	trace << "\ndelete widget" << endl;
	delete w;
	trace << "\nnew widget[25]" << endl;
	widget* wa = new widget[25];
	trace << "\ndelete []widget" << endl;
	delete []wa;

	return 0;
}</strong></span>

10.

<span style="font-size:18px;"><strong>/*构造函数调用*/
/*NOMEMORY.cpp*/
#include <iostream.h>
#include <new.h>
//当程序运行时,它仅打印来自运算符new的信息。因为new返回0,所以构造函数没有被调
//用,当然它的信息不会打印出来。
void my_new_handler()
{
	cout << "new handler called" << endl;
}

class nomemory
{
public:
	nomemory()
	{
		cout << "nomemory::nomemory()" << endl;
	}
	void* operator new(size_t sz)
	{
		cout << "nomemory::operator new" << endl;
		return 0;
	}
};

int main()
{
	set_new_handler(my_new_handler);
	nomemory* nm = new nomemory;
	cout << "nm = " << nm << endl;

	return 0;
}</strong></span>

11.

<span style="font-size:18px;"><strong>/*在一个特定的存储单元里放置一个对象*/
/*PLACEMNT.cpp*/
#include <stddef.h>
#include <iostream.h>

class X
{
	int i;
public:
	X(int I = 0)
	{
		i = I;
	}
	~X()
	{
		cout << "X::~X()" << endl;
	}
	void* operator new(size_t, void* loc)
	{
		return loc;
	}
};

int main()
{
	int l[10];
	X* xp = new(l) X(47);
	xp->X::~X();//显式地调用析构函数
//销毁对象时将会出现两难选择的局面。因为仅有一个版本运算符 delete,所以没有办法说
//“对这个对象使用我的特殊内存释放器”。我们可以调用析构函数,但不能用动态内存机制释放
//内存,因为内存不是在堆上分配的。
	return 0;
}</strong></span>

三.习题+解答

1. 写一个有构造函数和析构函数的类。在构造函数和析构函数里通过 cout宣布自己。通过这个类自己证明new和delete总是调用构造函数和析构函数。用 new创建这个类的一个对象,用delete销毁它。在堆上创建和销毁这些对象的一个数组。

#include <iostream.h>

class what
{
	int i;
public:
	what()
	{
		cout << "what::what()" << endl;
	}
	~what()
	{
		cout << "what::~what()" << endl;
	}
};

int main()
{
	what* w = new what;
	delete w;
	what* v = new what[10];
	delete []v;

	return 0;
}

运行结果如下:

2. 创建一个pstash对象,并把练习1的对象用new创建填入。观察当这个对象出了范围和它的析构函数被调用时发生的情况。

#include <iostream.h>
#include "E:\VC++\8_4_2\pstash.h"
#include "E:\VC++\8_4_2\pstash.cpp"

class what
{
	int i;
public:
	what()
	{
		cout << "what::what()" << endl;
	}
	~what()
	{
		cout << "what::~what()" << endl;
	}
};

int main()
{
	pstash p;
	for(int i = 0; i < 25; ++i)
	{
		p.add(new what);
	}
	//对象出了范围会继续分配直至内存耗尽
	//p.~pstash();//崩溃

	return 0;
}

3. 写一个有单个对象版本和数组版本的重载运算符new和delete的类。演示这两个版本的工作情况。

/*使用标准的C库函数中的时间函数来生成简单的 Time类*/
/*CPPTIME.h*/
#ifndef TEXT_H_
#define TEXT_H_
#include <time.h>
#include <string.h>
#include <iostream>
using namespace std;

class A
{
	int i;
	char c;
	float f;
	double d;
	int j;
	char n;
	float m;
	double g;
public:
	A()
	{
		i = j = 0;
		c = n = '0';
		f = m = 1.0;
		d = g = 0.0;
		cout << "Create A()!" << endl;
	}
	A(int I, char C, float F, double D, int J, char N, float M, double G);
};

A::A(int I, char C, float F, double D, int J, char N, float M, double G)
:i(I),c(C),f(F),d(D),j(J),n(N),m(M),g(G)
{
	cout << "Create A(int I, char C, float F, double D, int J, char N, float M, double G)!"
		<< endl;
}

class B
{
	A a;
	int flag;
	char name;
	double number;
	enum{
		size = 10
	};
	int arr[size];
public:
	friend A;
	B()
	{
		flag = 0;
		name = '0';
		number = 0.0;
		for(int k = 0,m = 0;k < size; ++k,++m)
		{
			arr[k] = m;
		}
		cout << "Create B()!" << endl;
	}
	B(int Flag, char Name, double Number);
	void g();
	void f()
	{
		cout << "f()" << " ";
	}
};

B::B(int Flag, char Name, double Number):flag(Flag),name(Name),number(Number)
{
	cout << "Create B(int Flag, char Name, double Number)!"
		<< endl;
}

void B::g()
{
	cout << "g()" << " ";
}

class Time
{
	time_t T;
	tm local;
	char Ascii[26];
	unsigned char lflag, aflag;
	void updateLocal()
	{
		if(!lflag)
		{
			local = *localtime(&T);
			lflag++;
		}
	}
	void updateAscii()
	{
		if(!aflag)
		{
			updateLocal();
			strcpy(Ascii, asctime(&local));
			aflag++;
		}
	}
public:
	Time()
	{
		mark();
	}
	void mark()
	{
		lflag = aflag = 0;
		time(&T);
	}
	const char* ascii()
	{
		updateAscii();
		return Ascii;
	}
	int delta(Time* dt) const
	{
		return difftime(T, dt->T);
	}
	int DaylightSavings()
	{
		updateLocal();
		return local.tm_isdst;
	}
	int DayOfYear()
	{
		updateLocal();
		return local.tm_yday;
	}
	int DayOfWeek()
	{
		updateLocal();
		return local.tm_wday;
	}
	int Since1900()
	{
		updateLocal();
		return local.tm_year;
	}
	int Month()
	{
		updateLocal();
		return local.tm_mon;
	}
	int DayOfMonth()
	{
		updateLocal();
		return local.tm_mday;
	}
	int Hour()
	{
		updateLocal();
		return local.tm_hour;
	}
	int Minute()
	{
		updateLocal();
		return local.tm_min;
	}
	int Second()
	{
		updateLocal();
		return local.tm_sec;
	}
};
#endif
/*两个私有函数updateLocal( )和 updateAscii( )检查标
记,并有条件地执行更新。*/

#include "E:\VC++\7_31_1\cpptime.h"

class why
{
	int i[10];
public:
	why()
	{
		cout << "why::why()" << endl;
	}
	~why()
	{
		cout << "why::~why()" << endl;
	}
	void* operator new(size_t sz)
	{
		cout << "why::new: "
			  << sz << "bytes" << endl;
		return ::new char[sz];
	}
	void operator delete(void* p)
	{
		cout << "why::delete" << endl;
		::delete []p;
	}
	void* operator new[](size_t sz)
	{
		cout << "why::new[]: "
			  << sz << " bytes" << endl;
		return ::new char[sz];
	}
	void operator delete[](void* p)
	{
		cout << "why::delete[]" << endl;
		::delete []p;
	}
};

int main()
{
	cout << "new why" << endl;
	Time start1;
	why* w = new why;
	cout << "\ndelete why" << endl;
	delete w;
	Time end1;
	cout << endl;
	cout << "start1 = " << start1.ascii();
	cout << "end1 = " << end1.ascii();
	cout << "delta1 = " << end1.delta(&start1) << endl;
	cout << "\nnew why[25]" << endl;
	Time start2;
	why* wa = new why[25];
	cout << "\ndelete []why" << endl;
	delete []wa;
	Time end2;
	cout << endl;
	cout << "start2 = " << start2.ascii();
	cout << "end2 = " << end2.ascii();
	cout << "delta2 = " << end2.delta(&start2) << endl;

	return 0;
}

4. 设计一个对FRAMIS.CPP进行测试的程序来显示定制的new和delete比全局的new和delete大约快多少。

/*使用标准的C库函数中的时间函数来生成简单的 Time类*/
/*CPPTIME.h*/
#ifndef TEXT_H_
#define TEXT_H_
#include <time.h>
#include <string.h>
#include <iostream>
using namespace std;

class A
{
	int i;
	char c;
	float f;
	double d;
	int j;
	char n;
	float m;
	double g;
public:
	A()
	{
		i = j = 0;
		c = n = '0';
		f = m = 1.0;
		d = g = 0.0;
		cout << "Create A()!" << endl;
	}
	A(int I, char C, float F, double D, int J, char N, float M, double G);
};

A::A(int I, char C, float F, double D, int J, char N, float M, double G)
:i(I),c(C),f(F),d(D),j(J),n(N),m(M),g(G)
{
	cout << "Create A(int I, char C, float F, double D, int J, char N, float M, double G)!"
		<< endl;
}

class B
{
	A a;
	int flag;
	char name;
	double number;
	enum{
		size = 10
	};
	int arr[size];
public:
	friend A;
	B()
	{
		flag = 0;
		name = '0';
		number = 0.0;
		for(int k = 0,m = 0;k < size; ++k,++m)
		{
			arr[k] = m;
		}
		cout << "Create B()!" << endl;
	}
	B(int Flag, char Name, double Number);
	void g();
	void f()
	{
		cout << "f()" << " ";
	}
};

B::B(int Flag, char Name, double Number):flag(Flag),name(Name),number(Number)
{
	cout << "Create B(int Flag, char Name, double Number)!"
		<< endl;
}

void B::g()
{
	cout << "g()" << " ";
}

class Time
{
	time_t T;
	tm local;
	char Ascii[26];
	unsigned char lflag, aflag;
	void updateLocal()
	{
		if(!lflag)
		{
			local = *localtime(&T);
			lflag++;
		}
	}
	void updateAscii()
	{
		if(!aflag)
		{
			updateLocal();
			strcpy(Ascii, asctime(&local));
			aflag++;
		}
	}
public:
	Time()
	{
		mark();
	}
	void mark()
	{
		lflag = aflag = 0;
		time(&T);
	}
	const char* ascii()
	{
		updateAscii();
		return Ascii;
	}
	int delta(Time* dt) const
	{
		return difftime(T, dt->T);
	}
	int DaylightSavings()
	{
		updateLocal();
		return local.tm_isdst;
	}
	int DayOfYear()
	{
		updateLocal();
		return local.tm_yday;
	}
	int DayOfWeek()
	{
		updateLocal();
		return local.tm_wday;
	}
	int Since1900()
	{
		updateLocal();
		return local.tm_year;
	}
	int Month()
	{
		updateLocal();
		return local.tm_mon;
	}
	int DayOfMonth()
	{
		updateLocal();
		return local.tm_mday;
	}
	int Hour()
	{
		updateLocal();
		return local.tm_hour;
	}
	int Minute()
	{
		updateLocal();
		return local.tm_min;
	}
	int Second()
	{
		updateLocal();
		return local.tm_sec;
	}
};
#endif
/*两个私有函数updateLocal( )和 updateAscii( )检查标
记,并有条件地执行更新。*/

#include <stddef.h>
#include "E:\VC++\7_31_1\cpptime.h"

class framis
{
	char c[10];
	static unsigned char pool[];
	static unsigned char alloc_map[];
public:
	enum{ psize = 100 };
	framis()
	{
		cout << "framis()\n";
	}
	~framis()
	{
		cout << "~framis()...";
	}
	void* operator new(size_t);
	void operator delete(void*);
};

unsigned char framis::pool[psize * sizeof(framis)];
unsigned char framis::alloc_map[psize] = {0};

void* framis::operator new(size_t)
{
	for(int i = 0; i < psize; ++i)
	{
		if(!alloc_map[i])
		{
			cout << "using block " << i << " ... ";
			alloc_map[i] = 1;
			return pool + (i * sizeof(framis));
		}
	}
	cout << "out of memory" << endl;
	return 0;
}

void framis::operator delete(void* m)
{
	if(!m)
	{
		return;
	}
	unsigned long block = (unsigned long)m
		- (unsigned long)pool;
	block /= sizeof(framis);
	cout << "freeing block " << block << endl;
	alloc_map[block] = 0;
}

int main()
{
	Time start1;
	framis* x = new framis;
	delete x;
	Time end1;
	cout << endl;
	cout << "start1 = " << start1.ascii();
	cout << "end1 = " << end1.ascii();
	cout << "delta1 = " << end1.delta(&start1) << endl;
	Time start2;
	framis* f[framis::psize];
	for(int i = 0; i < framis::psize; ++i)
	{
		f[i] = new framis;
	}
	for(int j = 0; j < framis::psize; ++j)
	{
		delete f[j];
	}
	Time end2;
	cout << endl;
	cout << "start2 = " << start2.ascii();
	cout << "end2 = " << end2.ascii();
	cout << "delta2 = " << end2.delta(&start2) << endl;

	return 0;
}

以上代码仅供参考,如有错误请大家指出,谢谢大家~

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-25 06:06:00

《C++编程思想》 第十二章 动态对象创建 (习题+解答)的相关文章

Java编程思想---第十二章 通过异常处理错误(中)

第十二章  通过异常处理错误(中) 12.4 创建自定义异常 我们不必拘泥于Java中已有的异常类型,Java提供的异常体系不可能预见所有的错误,所以可以自己定义异常类来表示程序中可能会遇到的特定问题:要自己定义异常类,必须从已有的异常类继承,最好是选择意思相近的异常类继承,建立新的异常类最简单的方法就是让编译器为你产生默认构造器,所以这几乎不用写多少代码: class SimpleException extends Exception { } public class InheritingEx

《C++编程思想》 第七章 常 量 (习题+解答)

一.相关知识点        在 C语言中可以选择这样书写: const bufsize:        这样写在C++中是不对的,而 C编译器则把它作为一个声明,这个声明指明在别的地方有存储分配.因为C默认const是外部连接的, C++默认const是内部连接的,这样,如果在 C++中想完成与C中同样的事情,必须用extern把连接改成外部连接: extern const bufsize;//declaration only 这种方法也可用在C语言中. 指向const的指针 使用指针定义的技

《C++编程思想》 第十一章 运算符重载 (习题+解答)

一.相关代码 1. /*运算符重载语法*/ /*OPOVER.cpp*/ /*这两个重载的运算符被定义为内联成员函数.对于二元运算符,单个参数是出现在运算符 右侧的那个.当一元运算符被定义为成员函数时,没有参数.成员函数被运算符左侧的对象调 用. 对于非条件运算符(条件运算符通常返回一个布尔值),如果两个参数是相同的类型,希 望返回和运算相同类型的对象或引用.如果它们不是相同类型,它作什么样的解释就取决于程 序设计者.用这种方法可以组合复杂的表达式: K += I + J ; 运算符+号产生一个

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

一.相关知识点 1.  在C++中,存取控制并不是面向对象的特征,但它为类的创建者提供了很有价值的访问控制.类的用户可以清楚地看到,什么可以用,什么应该忽略.更重要的是,它保证了类的用户不会依赖任何类的实现细节.有了这些,我们就能更改类的实现部分,没有人会因此而受到影响,因为他们并不能访问类的这一部分.一旦我们有了更改实现部分的自由,就可以在以后的时间里改进我们的设计,而且允许犯错误.要知道,无论我们如何小心地计划和设计,都可能犯错误.知道犯些错误也是相对安全的,这意味着我们会变得更有经验,会学

【读书笔记】C#高级编程 第十二章 动态语言扩展

(一)DLR C#4的动态功能是Dynamic Language Runtime(动态语言运行时,DLR)的一部分.DLR是添加到CLR的一系列服务. (二)dynamic类型 dynamic类型允许编写忽略编译期间的类型检查的代码.编译器假定,给dynamic类型的对象定义的任何操作都是有效的,在运行之前编译器不会检测是否存在错误. 例子: dynamic person = "人"; string firstName = person.FirstName; 这两行代码能够通过编译器编

第二十二章 动态分区管理(LPAR)

一.逻辑分区 Lpar即系统级的逻辑分区,它把一台计算机上的硬件资源划分成多个不同的逻辑服务器,每个逻辑服务器上单独运行一个私有的操作系统,这样就可以实现在一台服务器上多个操作系统的运行. 根据在逻辑分区中调配资源是否需要重启这个分区中的操作系统,可以把逻辑分区分成两种:静态Lpar和动态Lpar.静态Lpar是指系统资源(CPU.内存和I/O等)在不同的分区之间移动的时候需要重新启动所有影响到的Lpar,而动态Lpar则可以使用户在不同的分区之间灵活移动资源时不会影响到分区的正常运行,既不需要

Java编程思想学习(十二) 数组和容器

一.数组 1).数组的多种初始化方式 下面总结了初始化数组的多种方式,以及如何对指向数组的引用赋值,使其指向另一个数组对象.值得注意的是:对象数组和普通数组的各种操作基本上都是一样的:要说有什么不同的话就是对象数组默认值为null,而基本数组视本身情况而定. 1 package lkl; 2 3 import java.util.Arrays; 4 5 ///下面演示了数组的初始化 6 //包括对象数组和普通数组 7 class Base{ 8 private static long count

第十二章 动态内存与智能指针

动态内存与智能指针 [智能指针]头文件#include<memory>shared_ptr: 允许多个指针指向同一个对象unique_ptr: "独占"所指向的对象weak_ptr:伴随类,它是一种弱引用,指向shared_ptr所管理的对象. 原文地址:https://www.cnblogs.com/sunbines/p/8552298.html

Java编程思想之十二 通过异常处理错误

Java的基本概念是结构不佳的代码不能运行余下的问题必须在运行期间解决,这就需要错误源能通过某种方式,把适当的信息传递给某个接收者--该接收者将知道如何正确处理这里问题. 12.1 概念 使用异常所带来的另一个相当明显的好处,它往往能够降低错误处理代码的复杂度. 12.2 基本异常 异常情形是指阻止当前方法或作用域继续执行的问题.把异常情形与普通问题相区分很重要,普通问题是指,在当前环境下能得到足够的信息,总能处理这个错误.而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解