1. Trang chủ
  2. » Công Nghệ Thông Tin

C++ Primer Plus (P44) docx

20 348 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề C++ Primer Plus
Trường học University of Example
Chuyên ngành Computer Science
Thể loại Thesis
Năm xuất bản 2025
Thành phố Example City
Định dạng
Số trang 20
Dung lượng 182,79 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

1: Start with the following class declaration:// base class class Cd { // represents a CD disk private: char performers[50]; char label[20]; int selections; // number of selections do

Trang 1

1: Start with the following class declaration:

// base class class Cd { // represents a CD disk private:

char performers[50];

char label[20];

int selections; // number of selections double playtime; // playing time in minutes public:

Cd(char * s1, char * s2, int n, double x);

Cd(const Cd & d);

Cd();

~Cd();

void Report() const; // reports all CD data

Cd & operator=(const Cd & d);

};

Derive a Classic class that adds an array of char members that will hold a string identifying the primary work on the CD If the base class requires that any functions be virtual, modify the base class declaration to make it so If a declared method is not needed, remove it from the definition Test your product with the following program:

#include <iostream>

using namespace std;

#include "classic.h" // which will contain #include cd.h void Bravo(const Cd & disk);

int main() {

Cd c1("Beatles", "Capitol", 14, 35.5);

Classic c2 = Classic("Piano Sonata in B flat, Fantasia in C", "Alfred Brendel", "Philips", 2, 57.17);

Cd *pcd = &c1;

cout << "Using object directly:\n";

Trang 2

c1.Report(); // use Cd method c2.Report(); // use Classic method cout << "Using type cd * pointer to objects:\n";

pcd->Report(); // use Cd method for cd object pcd = &c2;

pcd->Report(); // use Classic method for classic object cout << "Calling a function with a Cd reference argument:\n";

Bravo(c1);

Bravo(c2);

cout << "Testing assignment: ";

Classic copy;

copy = c2;

copy.Report() return 0;

} void Bravo(const Cd & disk) {

disk.Report();

}

2: Repeat exercise 1, but this time use dynamic memory allocation instead of fixed-size arrays for the various strings tracked by the two classes

3: Revise the baseDMA-lacksDMA-hasDMA class hierarchy so that all three classes are derived from an abstract base class Test the result with a program similar to the one in Listing 13.10 That is, it should feature an array of pointers to the abstract base class and allow the user to make runtime decisions as to what types of objects are created

4: The Benevolent Order of Programmers maintains a collection of bottled port To describe it, the BOP Portmaster has devised a Port class as declared below:

#include <iostream>

using namespace std;

class Port

Trang 3

{ private:

char * brand;

char style[20]; // i.e., tawny, ruby, vintage int bottles;

public:

Port(const char * br = "none", const char * st = "none", int b = 0);

Port(const Port & p); // copy constructor virtual ~Port() { delete [] brand; }

Port & operator=(const Port & p);

Port & operator+=(int b); // adds b to bottles Port & operator-=(int b); // subtracts b from bottles, if //available

int BottleCount() const { return bottles; } virtual void Show() const;

friend ostream & operator<<(ostream & os, const Port & p);

};

The Show() method presents information in the following format:

Brand: Gallo Kind: tawny Bottles: 20

The operator<<() function presents information in the following format (no newline at the end):

Gallo, tawny, 20

The Portmaster completed the method definitions for the Port class and then derived the VintagePort class as follows before being relieved of his position for accidentally routing a bottle of '45 Cockburn to someone preparing an experimental barbecue sauce

class VintagePort : public Port // style necessarily = "vintage"

{

Trang 4

char * nickname; // i.e., "The Noble" or "Old Velvet", etc.

int year; // vintage year public:

VintagePort();

VintagePort(const char * br, int b, const char * nn, int y);

VintagePort(const VintagePort & vp);

~VintagePort() { delete [] nickname; } VintagePort & operator=(const VintagePort & vp);

void Show() const;

friend ostream & operator<<(ostream & os, const VintagePort & vp);

};

You get the job of completing the VintagePort work

Your first task is to re-create the Port method definitions, for the former Portmaster immolated his upon being relieved

a.

Your second task is to explain why certain methods are redefined and others are not

b.

Your third task is to explain why operator=() and operator<<() are not virtual

c.

Your fourth task is to provide definitions for the VintagePort methods

d.

CONTENTS

Trang 5

Chapter 14 REUSING CODE IN C++

You will learn about the following in this chapter:

Classes with Object Members Private Inheritance

Multiple Inheritance Class Templates Summary

Review Questions Programming Exercises

One of the main goals of C++ is to facilitate the reuse of code Public inheritance is one

mechanism for achieving this goal, but not the only one This chapter will investigate other

choices One technique is using class members that are themselves objects of another

class This is referred to as containment or composition or layering Another option is

using private or protected inheritance Containment, private inheritance, and protected

inheritance typically are used to implement has-a relationships, that is, relationships for

which the new class has an object of another class For example, a Stereo class might

have a CdPlayer object Multiple inheritance lets you create classes that inherit from two

or more base classes, combining their functionality

Chapter 10, "Objects and Classes," introduced function templates Now we'll look at class

templates, which provide another way of reusing code Class templates let you define a

class in generic terms Then you can use the template to create specific classes defined for

specific types For example, you could define a general stack template and then use the

template to create one class representing a stack of int values and another class

representing a stack of double values You could even generate a class representing a

stack of stacks

Classes with Object Members

Let's begin with classes that include class objects as members Some classes, such as the

Trang 6

String class of Chapter 12, "Classes and Dynamic Memory Allocation," or the standard

C++ classes and templates of Chapter 16, "The string Class and the Standard Template

Library," offer convenient ways of representing components of a more extensive class

We'll look at a particular example now

What is a student? Someone enrolled in a school? Someone engaged in thoughtful

investigation? A refugee from the harsh exigencies of the real world? Someone with an

identifying name and a set of quiz scores? Clearly, the last definition is a totally inadequate

characterization of a person, but it is well-suited for a simple computer representation So

let's develop a Student class based on that definition

Simplifying a student to a name and a set of quiz scores suggests using a String class

object (Chapter 12) to hold the name and an array class object (coming up soon) to hold

the scores (assumed to be type double) (Once you learn about the library classes

discussed in Chapter 16, you probably would use the standard string and vector classes.)

You might be tempted to publicly derive a Student class from these two classes That

would be an example of multiple public inheritance, which C++ allows, but it would be

inappropriate here The reason is that the relationship of a student to these classes doesn't

fit the is-a model A student is not a name A student is not an array of quiz scores What

we have here is a has-a relationship A student has a name, and a student has an array of

quiz scores The usual C++ technique for modeling has-a relationships is to use

composition or containment; that is, to create a class composed of, or containing, members

that are objects of another class For example, we can begin a Student class declaration

like this:

class Student

{

private:

String name; // use a String object for name

ArrayDb scores; // use an ArrayDb object for scores

.

};

As usual, the class makes the data members private This implies that the Student class

member functions can use the public interfaces of the String and ArrayDb (for array of

double) classes to access and modify the name and scores objects, but that the outside

world cannot do so The only access the outside world will have to name and scores is

through the public interface defined for the Student class (see Figure 14.1) A common

Trang 7

way of describing this is saying that the Student class acquires the implementation of its

member objects, but doesn't inherit the interface For example a Student object uses the

String implementation rather than a char * name or a char name[26] implementation for

holding the name But a Student object does not innately have the ability to use the String

operator==() function

Figure 14.1 Containment.

Interfaces and Implementations

With public inheritance, a class inherits an interface, and, perhaps, an implementation (Pure virtual functions in a base class can provide an interface without an

implementation.) Acquiring the interface is part of the is-a relationship With composition, on the other hand, a class acquires the implementation without the interface Not inheriting the interface is part of the has-a relationship

Trang 8

The fact that a class object doesn't automatically acquire the interface of a contained

object is a good thing for a has-a relationship For example, one could extend the String

class to overload the + operator to allow concatenating two strings, but, conceptually, it

doesn't make sense to concatenate two Student objects That's one reason not to use

public inheritance in this case On the other hand, parts of the interface for the contained

class may make sense for the new class For example, you might want to use the

operator<() method from the String interface to sort Student objects by name You can

do so by defining a Student::Operator<() member function that, internally, uses the

String::Operator<() function Let's move on to some details

The first detail is developing the ArrayDb class so that the Student class can use it This

class will be quite similar to the String class because the latter is an array, too, in this

case, of char First, let's list some necessary and/or desirable features for the ArrayDb

class

It should be able to store several double values

It should provide random access to individual values using bracket notation with an index

One can assign one array to another

The class will perform bounds checking to ensure that array indices are valid

The first two features are the essence of an array The third feature is not true of built-in

arrays but is true for class objects, so creating an array class will provide that feature The

final feature, again, is not true for built-in arrays, but can be added as part of the second

feature

At this point, much of the design can ape the String declaration, replacing type char with

type double That is, you can do this:

class ArrayDb

{

private:

Trang 9

unsigned int size; // number of array elements

double * arr; // address of first element

public:

ArrayDb(); // default constructor

// create an ArrayDb of n elements, set each to val

ArrayDb(unsigned int n, double val = 0.0);

// create an ArrayDb of n elements, initialize to array pn

ArrayDb(const double * pn, unsigned int n);

ArrayDb(const ArrayDb & a); // copy constructor

virtual ~ArrayDb(); // destructor

double & operator[](int i); // array indexing

const double & operator[](int i) const; // array indexing

// other stuff to be added here

ArrayDb & operator=(const ArrayDb & a);

friend ostream & operator<<(ostream & os, const ArrayDb & a);

};

The class will use dynamic memory allocation to create an array of the desired size

Therefore, it also will provide a destructor, a copy constructor, and an assignment

operator For convenience, it will have a few more constructors

Tweaking operator[]()

To provide random access using array notation, the class has to overload the [] operator,

just as the String class did This will allow code like the following:

ArrayDb scores(5, 20.0); // 5 elements, each set to 20.0

double temp = scores[3];

scores[3] = 16.5:

Clients of the ArrayDB class can access array elements individually only through the

overloaded [] operator This gives you the opportunity to build in some safety checks In

particular, the method can check to see if the proposed array index is in bounds That is,

you can write the operator this way:

double & ArrayDb::operator[](int i)

{

Trang 10

// check index before continuing

if (i < 0 || i >= size)

{

cerr << "Error in array limits: "

<< i << " is a bad index\n";

exit(1);

}

return arr[i];

}

This slows down a program, for it requires evaluating an if statement every time the

program accesses an array element But it adds safety, preventing a program from placing

a value in the 2000th element of a 5-element array

As with the String class, we need a const version of the [] operator to allow read-only

access for constant ArrayDb objects:

const double & ArrayDb::operator[](int i) const

{

// check index before continuing

if (i < 0 || i >= size)

{

cerr << "Error in array limits: "

<< i << " is a bad index\n";

exit(1);

}

return arr[i];

}

The compiler will select the const version of operator[]() for use with const ArrayDb

objects and use the other version for non-const ArrayDb objects

Listing 14.1 shows the header file for the ArrayDb class For extra convenience, the class

definition includes an Average() method that returns the average of the array elements

Listing 14.1 arraydb.h

Trang 11

// arraydb.h array class

#ifndef ARRAYDB_H_

#define ARRAYDB_H_

#include <iostream>

using namespace std;

class ArrayDb

{

private:

unsigned int size; // number of array elements

double * arr; // address of first element

public:

ArrayDb(); // default constructor

// create an ArrayDb of n elements, set each to val

explicit ArrayDb(unsigned int n, double val = 0.0);

// create an ArrayDb of n elements, initialize to array pn

ArrayDb(const double * pn, unsigned int n);

ArrayDb(const ArrayDb & a); // copy constructor

virtual ~ArrayDb(); // destructor

unsigned int ArSize() const {return size;}// returns array size

double Average() const; // return array average

// overloaded operators

double & operator[](int i); // array indexing

const double & operator[](int i) const; // array indexing

ArrayDb & operator=(const ArrayDb & a);

friend ostream & operator<<(ostream & os, const ArrayDb & a);

};

#endif

Compatibility Note

Older implementations don't support the explicit keyword

Ngày đăng: 07/07/2014, 06:20

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN