《C++编程思想》 第十三章 继承和组合 (原书代码+习题+解答)

一.相关知识

使用其他人已经创建并调试过的类:

关键是使用类而不是更改已存在的代码。这一章将介绍两种完成这件事的方法。第一种方法是很直接的:简单地创建一个包含已存在的类对象的新类,这称为组合,因为这个新类是由已存在类的对象组合的。

第二种方法更巧妙,创建一个新类作为一个已存在类的类型,采取这个已存在类的形式,对它增加代码,但不修改它。这个有趣的活动被称为继承,其中大量的工作由编译器完成。继承是面向对象程序设计的基石,并且还有另外的含义,将在下一章中探讨。

对于组合和继承(感觉上,它们都是由已存在的类型产生新类型的方法),它们在语法上和行为上是类似的。这一章中,读者将学习这些代码重用机制。

二.相关代码

1.

<span style="font-size:18px;"><strong>/*USEFUL.cpp*/
#ifndef USEFUL_H_
#define USEFUL_H_

class X
{
	int i;
	enum{ factor = 11 };
public:
	X()
	{
		i = 0;
	}
	void set(int I)
	{
		i = I;
	}
	int read() const
	{
		return i;
	}
	int permute()
	{
		return i = i * factor;
	}
};
#endif</strong></span>

2.

<span style="font-size:18px;"><strong>/*COMPOSE.h*/
//在这个类中,数值成员是私有的,所以对于将类型 X 的一个对象作为公共对象嵌入到一个新
//类内部,是绝对安全的。
#include "useful.h"

class Y
{
	int i;
public:
	X x;
	Y()
	{
		i = 0;
	}
	void f(int I)
	{
		i = I;
	}
	int g() const
	{
		return i;
	}
};

int main()
{
	Y y;
	y.f(47);
	y.x.set(37);

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

3.

<span style="font-size:18px;"><strong>/*对于新类的public接口函数,包含对嵌入对象的使用,但不必模仿这个嵌入对象的接口。*/
/*COMPOSE2.h*/
//这里,permute()函数的执行调用了X的接口,而X的其他成员函数也在Y的成员函数中被调用。
#include "useful.h"

class Y
{
	int i;
	X x;
public:
	Y()
	{
		i = 0;
	}
	void f(int I)
	{
		i = I;
		x.set(I);
	}
	int g() const
	{
		return i * x.read();
	}
	void permute()
	{
		x.permute();
	}
};

int main()
{
	Y y;
	y.f(47);
	y.permute();

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

4.

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

class Y : public X
{
	int i;
public:
	Y()
	{
		i = 0;
	}
	int change()
		//在change()中,基类permute()函数被调用,派生类对所有的public基类函数都有直接的访问权
	{
		i = permute();
		return i;
	}
	void set(int I)
		//在派生类中的set()函数重定义了基类中的set()函数
		//这就是,如果对于类型 Y 的对象调用函数read()和permute(),得到的是这些函数的基类版本
		//(可以在main()中看到表现)。但如果对于对象 Y 调用set(),得到的是重定义的版本。这意
		//味着,如果我们不喜欢在继承中得到某个函数的基类版本,可以改变它(还能够增加全新的函
		//数,例如 change())。
	{
		i = I;
		X::set(I);
	}
};

int main()
{
	cout << "sizeof(X) = " << sizeof(X) << endl;
	cout << "sizeof(Y) = " << sizeof(Y) << endl;
	Y D;
	D.change();
	D.read();
	D.permute();
	D.set(12);

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

5.

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

class A
{
	int i;
public:
	A(int I)
	{
		i = I;
	}
	~A(){}
	void f() const{}
};

class B
{
	int i;
public:
	B(int I)
	{
		i = I;
	}
	~B(){}
	void f() const {}
};

class C : public B
{
	A a;
	//C 继承 B 并且有一个成员对象(这是类 A的对象)
public:
	C(int I):B(I), a(I){}
	//构造函数的初始化表达式表中调用了基类构造函数和成员对象构造函数。
	~C(){}
	void f() const
		//函数C::f()重定义了它所继承的B::f(),并且还调用基类版本,它还调用了a.f()
	{
		a.f();
		B::f();
	}
};

int main()
{
	C c(47);
}</strong></span>

6.

<span style="font-size:18px;"><strong>/*ORDER.cpp*/
/*可以看出,构造在类层次的最根处开始,而在每一层,首先调用基类构造函数,然后调用
成员对象构造函数。调用析构函数则严格按照构造函数相反的次序—这是很重要的,因为要
考虑潜在的相关性。*/
#include <fstream.h>
ofstream out("order.out");

#define CLASS(ID) class ID{public:  ID(int){ out << #ID " constructor\n";}  ~ID(){ out << #ID " destructor\n";}};

CLASS(base1);
CLASS(member1);
CLASS(member2);
CLASS(member3);
CLASS(member4);

class derived1 : public base1
{
	member1 m1;
	member2 m2;
public:
	derived1(int) : m2(1), m1(2), base1(3)
	{
		out << "derived1 constructor\n";
	}
	~derived1()
	{
		out << "derived1 destructor\n";
	}
};

class derived2 : public derived1
{
	member3 m3;
	member4 m4;
public:
	derived2() : m3(1), derived1(2), m4(3)
	{
		out << "derived2 constructor\n";
	}
	~derived2()
	{
		out << "derived2 destructor\n";
	}
};

int main()
{
	derived2 d2;

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

7.

<span style="font-size:18px;"><strong>/*名字隐藏
如果在基类中有一个函数名被重载几次,在派生类中重定义这个函数名会掩盖所有基类版
本,这也就是说,它们在派生类中变得不再可用。*/
/*HIDE.cpp*/
/*因为 bart 重定义了 d o h ( ),这些基类版本中没有一个是对于 bart 对象可调用的。这时,编译
器试图变换参数成为一个 milhouse 对象,并报告出错,因为它不能找到这样的变换。*/
#include <iostream.h>
class homer
{
public:
	int doh(int) const
	{
		return 1;
	}
	char doh(char) const
	{
		return 'd';
	}
	float doh(float) const
	{
		return 1.0;
	}
};

class bart : public homer
{
public:
	class milhouse{};
	void doh(milhouse) const {}
};

int main()
{
	bart b;
    //!b.doh(1);
	//!b.doh('x');
	//!b.doh(1.0);
}</strong></span>

8.

<span style="font-size:18px;"><strong>/*由编译器创建而不是继承的函数*/
/*NINHERIT.cpp*/
#include <fstream.h>
ofstream out("ninherit.out");

class root
{
public:
	root()
	{
		out << "root()\n";
	}
	root(root&)
	{
		out << "root(root&)\n";
	}
	root(int)
	{
		out << "root(int)\n";
	}
	root& operator=(const root&)
	{
		out << "root::operator=()\n";
		return *this;
	}
	class other{};
	operator other() const
		//operator other() 完成自动类型变换,从 root 对象到被嵌入的类 other 的对象
	{
		out << "root::operator other()\n";
		return other();
	}
	~root()
	{
		out << "~root()\n";
	}
};

class derived : public root {};
//类 derived 直接从root 继承,并没有创建函数(观察编译器如何反应)
//在 derived 中, operator=( ) 也被综合为新函数,使用成员函数赋值,因为这个函数在新类中不显式地写出

void f(root::other){}
//函数 f() 取一个 other 对象以测试这个自动类型变换函数

int main()
{
	derived d1;
	derived d2 = d1;
	//!derived d3(1);
	d1 = d2;
	f(d1);

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

9.

<span style="font-size:18px;"><strong>/*组合通常在希望新类内部有已存在类性能时使用,而不希望已存在类作为它的接口。这就
是说,嵌入一个计划用于实现新类性能的对象,而新类的用户看到的是新定义的接口而不是来
自老类的接口。为此,在新类的内部嵌入已存在类的 private 对象。
有时,允许类用户直接访问新类的组合是有意义的,这就让成员对象是 public。成员函数
隐藏它们自己的实现,所以,当用户知道我们正在装配一组零件并且使得接口对他们来说更容
易理解时,这样会安全的*/
/*CAR.cpp*/
//稍加思考就会看到,用车辆对象组合小汽车是无意义的—小汽车不能包含车辆,它本身
//就是一种车辆。这种is-a关系用继承表达,而 has-a 关系用组合表达。
#include <iostream.h>

class engine
{
public:
	void start() const {}
	void rev() const {}
	void stop() const {}
};

class wheel
{
public:
	void inflate(int psi) const {}
};

class window
{
public:
	void rollup() const {}
	void rolldown() const {}
};

class door
{
public:
	window Window;
	void open() const {}
	void close() const {}
};

class car
{
public:
	engine Engine;
	wheel Wheel[4];
	door left, right;
};

int main()
{
	car Car;
	Car.left.Window.rollup();
	Car.Wheel[0].inflate(72);

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

10.

<span style="font-size:18px;"><strong>/*子类型设置*/
/*FNAME1.cpp*/
#include <fstream.h>
#include <strstrea.h>
#include "E:\VC++\7_31_2\allege.h"

class fname1
{
	ifstream File;
	enum{ bsize = 100 };
	char buf[bsize];
	ostrstream Name;
	int nameset;
public:
	fname1() : Name(buf, bsize), nameset(0) {}
	fname1(const char* filename) : File(filename), Name(buf, bsize)
	{
		allegefile(File);
		Name << filename << ends;
		nameset = 1;
	}
	const char* name() const
	{
		return buf;
	}
	void name(const char* newname)
	{
		if(nameset)
		{
			return;
		}
		Name << newname << ends;
		nameset = 1;
	}
	operator ifstream&()
	{
		return File;
	}
};

int main()
{
	fname1 file("fname1.cpp");
	cout << file.name() << endl;
	//Error:rdbuf() not a member
	//!cout << file.rdbuf() << endl;//因为自动类型转换只发生在函数调用中,而不在成员选择期间

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

11.

<span style="font-size:18px;"><strong>/*子类型设置*/
/*FNAME2.cpp*/
#include <fstream.h>
#include <strstrea.h>
#include "E:\VC++\7_31_2\allege.h"

class fname2 : public ifstream
{
	enum{ bsize = 100 };
	char buf[bsize];
	ostrstream Name;
	int nameset;
public:
	fname2() : Name(buf, bsize), nameset(0) {}
	fname2(const char* filename) : ifstream(filename), Name(buf, bsize)
	{
		Name << filename << ends;
		nameset = 1;
	}
	const char* name() const
	{
		return buf;
	}
	void name(const char* newname)
	{
		if(nameset)
		{
			return;
		}
		Name << newname << ends;
		nameset = 1;
	}
};

int main()
{
	fname2 file("fname2.cpp");
	allegefile(file);
	cout << "name: " << file.name() << endl;
	const bsize = 100;
	char buf[bsize];
	file.getline(buf, bsize);
	file.seekg(-200, ios::end);
	cout << file.rdbuf() << endl;

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

12.

<span style="font-size:18px;"><strong>/*继承也就是取一个已存在的类,并制作它的一个专门的版本*/
/*INHSTAK.cpp*/
#include "E:\VC++\8_4_3\stackl1.h"
#include "E:\VC++\8_4_1\strings.h"
#include <fstream.h>
#include "E:\VC++\7_31_2\allege.h"

class Stringlist : public stack
{
public:
	void push(String* str)
	{
		stack::push(str);
	}
	String* peek() const
	{
		return (String*)stack::peek();
	}
	String* pop()
	{
		return (String*)stack::pop();
	}
};

int main()
{
	ifstream file("text.cpp");
	allegefile(file);
	const bufsize = 100;
	char buf[bufsize];
	Stringlist textlines;
	while(file.getline(buf, bufsize))
	{
		textlines.push(String::make(buf));
	}
	String* s;
	while((s = textlines.pop()) != 0)
	{
		cout << *s << endl;
	}

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

13.

<span style="font-size:18px;"><strong>/*对私有继承成员公有化
当私有继承时,基类的所有 public成员都变成了 private。如果希望它们中的任何一个是可
视的,只要用派生类的public选项声明它们的名字即可。*/
/*PRIVING.cpp*/
#include <iostream.h>

class base1
{
public:
	char f() const
	{
		return 'a';
	}
	int g() const
	{
		return 2;
	}
	float h() const
	{
		return 3.0;
	}
};

class derived : base1
{
public:
	base1::f;
	base1::h;
};

int main()
{
	derived d;
	d.f();
	d.h();
	//!d.g();

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

14.

<span style="font-size:18px;"><strong>/*数据成员最好是private,因为我们应该保留改变内部实现的权利。然后我们才能通过保护
成员函数控制对该类的继承者的访问。*/
/*PROTECT.cpp*/
/*被保护的继承
继承时,基类缺省为 private,这意味着所有 public成员函数对于新类的用户是 private的。
通常我们都会让继承 public,从而使得基类的接口也是派生类的接口。然而在继承期间,也可
以使用protected关键字。
被保护的派生意味着对其他类来“照此实现”,但对派生类和友元是“is-a”。它是不常用
的,它的存在只是为了语言的完整性。*/
#include <fstream.h>

class base
{
	int i;
protected:
	int read() const
	{
		return i;
	}
	void set(int I)
	{
		i = I;
	}
public:
	base(int I = 0) : i(I) {}
	int value(int m) const
	{
		return m * i;
	}
};

class derived : public base
{
	int j;
public:
	derived(int J = 0) : j(J) {}
	void change(int x)
	{
		set(x);
	}
};

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

15.

<span style="font-size:18px;"><strong>/*向上映射*/
/*WIND.cpp*/
/*在tune()中,这些代码对instrument和从instrument派生来的任何类型都有效,这种将 wind的对象、
引用或指针转变成instrument对象、引用或指针的活动称为向上映射。*/
#include <iostream.h>

enum note { middleC, Csharp, Cflat };

class instrument
{
public:
	void play(note) const {}
};

class wind : public instrument {};

void tune(instrument& i)
{
	i.play(middleC);
}

int main()
{
	wind flute;
	tune(flute);
	//wind w;
	//instrument* ip = &w;
	//instrument& ir = w;
	//编译器只能把ip作为一个instrument指针处理。这就是说,它不知道 ip实际上指向wind的对象。
    //所以,当调用play()成员函数时,如果使用
    //ip->play(middleC) ;
    //编译器只知道它正在对于一个instrument指针调用play(),并调用instrument∷play()的基本版本,
    //而不是它应该做的调用wind∷play()。这样将会得到不正确的结果。

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

16.

<span style="font-size:18px;"><strong>/*stringlist对象仅作为string包容器,不需向上映射,所以更合适的方法可能是组合*/
/*INHSTAK2.cpp*/

#include "E:\VC++\8_4_3\stackl1.h"
#include "E:\VC++\8_4_1\strings.h"
#include <fstream.h>
#include "E:\VC++\7_31_2\allege.h"

class Stringlist
{
	stack Stack;
public:
	void push(String* str)
	{
		Stack.push(str);
	}
	String* peek() const
	{
		return (String*)Stack.peek();
	}
	String* pop()
	{
		return (String*)Stack.pop();
	}
};

int main()
{
	ifstream file("text.cpp");
	allegefile(file);
	const bufsize = 100;
	char buf[bufsize];
	Stringlist textlines;
	while(file.getline(buf, bufsize))
	{
		textlines.push(String::make(buf));
	}
	String* s;
	while((s = textlines.pop()) != 0)
	{
		cout << *s << endl;
	}

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

三.习题+解答

1. 修改CAR.CPP,使得它也从被称为 vehicle的类继承,在 vehicle中放置合适的成员函数(也就是说,补充一些成员函数)。对vehicle增加一个非缺省的构造函数,在car的构造函数内部必须调用它。

#include <iostream.h>

class engine
{
public:
	void start() const {}
	void rev() const {}
	void stop() const {}
};

class wheel
{
public:
	void inflate(int psi) const {}
};

class window
{
public:
	void rollup() const {}
	void rolldown() const {}
};

class door
{
public:
	window Window;
	void open() const {}
	void close() const {}
};

class vehicle
{
public:
	vehicle()
	{}
	void g(){}
	void f(){}
};

class car : vehicle
{
public:
	engine Engine;
	wheel Wheel[4];
	door left, right;
	car()
	{
		vehicle();
	}
};

int main()
{
	car Car;
	Car.left.Window.rollup();
	Car.Wheel[0].inflate(72);

	return 0;
}

2. 创建两个类, A和B,带有能宣布自己的缺省构造函数。从 A继承出一个新类,称为 C,并且在C中创建B的一个成员对象,而不对C创建构造函数。创建类C 的一个对象,观察结果。

#include <iostream.h>

class A
{
	int i;
public:
	A(int I = 0)
	{
		i = I;
		cout << "A::A(int I)" << endl;
	}
};

class B
{
	int j;
public:
	B(int J = 0)
	{
		j = J;
		cout << "B::B(int J)" << endl;
	}
};

class C : public A
{
public:
	B b;
};

int main()
{
	C c;

	return 0;

调用结果:

3. 使用继承,专门化在第 1 2章( PSTASH.H & PSTASH. CPP)中的pstash类,使得它接受和返回String针。修改PSTEST. CPP并测试它。改变这个类使得pstash是一个成员对象。

/*PSTASH.h*/
#include "E:\VC++\8_4_1\strings.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;
	}
};

class Stringlist : public pstash
{
public:
	int add(String* element)
	{
		return pstash::add(element);
	}
	String* operator[](int index) const
	{
		return (String*)pstash::operator[](index);
	}
	int count() const
	{
		return pstash::count();
	}
};

#endif
/*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;
}
/*PSTEST.cpp*/
#include "pstash.h"
#include <fstream.h>
#include "E:\VC++\7_31_2\allege.h"

int main()
{
	ifstream file("pstest.cpp");
	allegefile(file);
	const bufsize = 80;
	char buf[bufsize];
	Stringlist textlines;
	while(file.getline(buf, bufsize))
	{
		textlines.add(String::make(buf));
	}

	return 0;
}

4. 使用private和protected继承从基类创建两个新类。然后尝试向上映射这个派生类的对象成为基类。解释所发生的事情。

#include <iostream.h>

enum note { middleC, Csharp, Cflat };

class A
{
public:
	void play(note) const {}
};

class B : private A {};

class C : protected A {};

void tune(A& i)
{
	i.play(middleC);
}

int main()
{
	B flute1;
	//!tune(flute1);
	//error C2243: 'type cast' : conversion from 'class B *' to 'class A &' exists, but is inaccessible
	C flute2;
	//!tune(flute2);
	//error C2243: 'type cast' : conversion from 'class C *' to 'class A &' exists, but is inaccessible

	return 0;
}

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

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

时间: 2024-10-30 12:21:24

《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&quo

《C++编程思想》 第九章 命 名 控 制 (知识点+习题+解答)

一.相关知识点         那些通常放在头文件里的名字,像常量.内联函数(inline function),在缺省情况下都是内部连接的(当然常量只有在C + +中缺省情况下是内部连接的,在 C中它缺省为外部连接).注意连接只引用那些在连接/装载期间有地址的成员,因此类声明和局部变量并没有连接. 名字空间的产生与一个类的产生非常相似: namespace MyLib{ //Declarations } 这就产生了一个新的名字空间,其中包含了各种声明.namespace与class.struct

c++编程思想---第14章 继承和组合

1,实现代码重用两种方式 组合:我简单的在新类中创建一个已存在的类的对象.因为新类是由已存在类的对象组合而成,称之为组合. 这样就可以把已存在类的功能加到了新的类中去. 继承:扩展父类. 2,基类的private成员,只能通过基类提供的接口来访问. 3,调用基类的函数,隐藏的或者覆盖的可以通过作用域运算符来访问. 4,重载 (1)相同的范围(在同一个类中) (2)函数名字相同 (3)参数不同 (4)virtual 关键字可有可无 覆盖(派生类函数覆盖基类函数) (1)不同的范围(分别位于派生类与

《C++编程思想》 第十四章 多态和虚函数 (原书代码+习题+讲解)

一.相关知识点 函数调用捆绑 把函数体与函数调用相联系称为捆绑(binding).当捆绑在程序运行之前(由编译器和连接器)完成时,称为早捆绑.我们可能没有听到过这个术语,因为在过程语言中是不会有的:C编译只有一种函数调用,就是早捆绑.上面程序中的问题是早捆绑引起的,因为编译器在只有 instrument地址时它不知道正确的调用函数.解决方法被称为晚捆绑,这意味着捆绑在运行时发生,基于对象的类型.晚捆绑又称为动态捆绑或运行时捆绑.当一个语言实现晚捆绑时,必须有一种机制在运行时确定对象的类型和合适的

《C++编程思想》第八章 内 联 函 数 (知识点+习题+解答)

一.相关知识点 任何在类中定义的函数自动地成为内联函数,但也可以使用inline关键字放在类外定义的函数前面使之成为内联函数.但为了使之有效,必须使函数体和声明结合在一起,否则,编译器将它作为普通函数对待.因此 inline int PlusOne(int x); 没有任何效果,仅仅只是声明函数(这不一定能够在稍后某个时候得到一个内联定义).成功的方法如下: inline int PlusOne(int x) { return ++x ;} 在头文件里,内联函数默认为内部连接--即它是 stat

《C++编程思想》第二章 数 据 抽 象(原书代码+习题+答案)

相关代码例如以下: 1. <span style="font-size:18px;">/*声明与定义的差别*/ #include <iostream> using namespace std; extern int i;//声明 extern float f(float);//声明 float b;//定义+声明 float f(float a)//定义 { return a + 1.0; } int i;//定义 int h(int x)//定义+声明 { r

《C++编程思想》 第十章 引用和拷贝构造函数(知识点+习题+解答)

一.相关知识点 使用引用时有一定的规则: 1) 当引用被创建时,它必须被初始化.(指针则可以在任何时候被初始化.) 2) 一旦一个引用被初始化为指向一个对象,它就不能被改变为对另一个对象的引用.(指针则可以在任何时候指向另一个对象.) 3) 不可能有NULL引用.必须确保引用是和一块合法的存储单元关连. 仅当准备用传值的方式传递类对象时,才需要拷贝构造函数.如果不需要这么做,就不要拷贝构造函数. 对于指向一个对象的指针新的语法变为 ->*,对于一个对象或引用则为.*. 二.相关代码 1. /*R

java编程思想笔记(第一章)

Alan Kay 第一个定义了面向对象的语言 1.万物皆对象 2.程序是对象的集合,他们彼此通过发送消息来调用对方. 3.每个对象都拥有由其他对象所构成的存储 4.每个对象都拥有其类型(TYpe) 5.某一特定类型的所有对象都可以接收同样的消息. Booch提出一种更简洁的描述: 对象拥有状态(state) 行为(behavior) 和标识(identity) 每个对象都有一个接口 每个对象都属于定义了特性和行为的某个类(特性可以理解为属性的状态,行为可以理解为method) 在面向对象的程序设

Java编程思想——第17章 容器深入研究(two)

六.队列 排队,先进先出.除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: add 在尾部增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常 remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementExce