Skip to content

Commit

Permalink
完成第十二章
Browse files Browse the repository at this point in the history
  • Loading branch information
ShujiaHuang committed Aug 14, 2021
1 parent 015d819 commit 88ff710
Show file tree
Hide file tree
Showing 13 changed files with 783 additions and 0 deletions.
54 changes: 54 additions & 0 deletions practice/booknotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3965,4 +3965,58 @@ operator type_name();
### 12.6.3 其构造函数使用new的类
## 12.7 队列模拟
这里不记录了。
对于`const`数据成员,必须在执行到构造函数体之前,即创建对象时进行初始化。C++提供了一种特殊的语法来完成上 述工作,它叫做成员初始化列表(member initializer list)。成员初始化 列表由逗号分隔的初始化列表组成(前面带冒号)。它位于参数列表的 右括号之后、函数体左括号之前。如果数据成员的名称为 `mdata`,并需 要将它初始化为`val`,则初始化器为`mdata(val)`。
![image-20210814205323898](https://static.fungenomics.com/images/2021/08/image-20210814205323898.png)
**只有构造函数可以使用这种初始化列表语法**。如上所示,对于const 类成员,必须使用这种语法。另外,**对于被声明为引用的类成员,也必须使用这种语法**:
![image-20210814205737591](https://static.fungenomics.com/images/2021/08/image-20210814205737591.png)
**这是因为引用与const数据类似,只能在被创建时进行初始化**。对于 简单数据成员(例如front和items),使用成员初始化列表和在函数体中 使用赋值没有什么区别。
> 【注意】:
> 这种格式只能用于构造函数;
> 必须用这种格式来初始化非静态const数据成员;
> 必须用这种格式来初始化引用数据成员。
**不能将成员初始化列表语法用于构造函数之外的其他类方法**。
如果我们不希望复制构造函数被调用,也不允许赋值运算,可以这样做:
![image-20210814214006080](https://static.fungenomics.com/images/2021/08/image-20210814214006080.png)
这是一种禁用方法的技巧,同时可以作为一种暂时不编写这两个函数的预防措施:与其将来面对无法预料的运行故障,不如得到一个易于跟踪的编译错误,指出这些方法是不可访问的。另外,在定义其对象不允许 被复制的类时,这种方法也很有用。
还有没有其他影响需要注意呢?当然有。当对象被按值传递(或返 回)时,复制构造函数将被调用。然而,如果遵循优先采用按引用传递 对象的惯例,将不会有任何问题。另外,复制构造函数还被用于创建其 他的临时对象,但Queue定义中并没有导致创建临时对象的操作,例如 重载加法运算符。
## 12.8 总结
本章介绍了定义和使用类的许多重要方面。其中的一些方面是非常 微妙甚至很难理解的概念。
在类构造函数中使用new,也可能在对象过期 时引发问题。如果对象包含成员指针,同时它指向的内存是由new分配 的,则释放用于保存对象的内存并不会自动释放对象成员指针指向的内 存。因此在类构造函数中使用new类来分配内存时,应在类析构函数中 使用delete来释放分配的内存。这样,当对象过期时,将自动释放其指 针成员指向的内存。
如果对象包含指向new分配的内存的指针成员,则将一个对象初始 化为另一个对象,或将一个对象赋给另一个对象时,也会出现问题。
在 默认情况下,C++逐个对成员进行初始化和赋值,这意味着被初始化或 被赋值的对象的成员将与原始对象完全相同。如果原始对象的成员指向 一个数据块,则副本成员将指向同一个数据块。当程序最终删除这两个 对象时,类的析构函数将试图删除同一个内存数据块两次,这将出错。解决方法是:**定义一个特殊的复制构造函数来重新定义初始化,并重载赋值运算符**。
这样,旧对象和新对象都将引用独立 的、相同的数据,而不会重叠。由于同样的原因,必须定义赋值运算 符。对于每一种情况,最终目的都是执行深度复制,也就是说,复制实际的数据,而不仅仅是复制指向数据的指针。
**C++允许在类中包含结构、类和枚举定义。这些嵌套类型的作用域为整个类**,这意味着它们被局限于类中,不 会与其他地方定义的同名结构、类和枚举发生冲突。
C++为**类构造函数**提供了一种可用来**初始化数据成员的特殊语法**。 这种语法包括冒号和由逗号分隔的初始化列表,被放在构造函数参数的 右括号后,函数体的左括号之前。每一个初始化器都由被初始化的成员 的名称和包含初始值的括号组成。从概念上来说,这些初始化操作是在 对象创建时进行的,此时函数体中的语句还没有执行。语法如下:
```
queue(int qs): qsize(qs), items(0), front(NULL), rear(NULL) {}
```
如果数据成员是**非静态const成员或引用,则必须采用这种格式**,但 可将C++11新增的类内初始化用于非静态const成员。
99 changes: 99 additions & 0 deletions source/chapter12/bank.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// bank.cpp -- using the Queue interface
// compile with queue.cpp
#include <iostream>
#include <cstdlib> // for rand() and srand()
#include <ctime> // for time()
#include "queue.h"
const int MIN_PER_HR = 60;

bool newcustomer(double x); // is there a new customer?

int main()
{
using std::cin;
using std::cout;
using std::endl;
using std::ios_base;
// setting things up
std::srand(std::time(0)); // random initializing of rand()

cout << "Case Study: Bank of Heather Automatic Teller\n";
cout << "Enter maximum size of queue: ";
int qs;
cin >> qs;
Queue line(qs); // line queue holds up to qs people

cout << "Enter the number of simulation hours: ";
int hours; // hours of simulation
cin >> hours;
// simulation will run 1 cycle per minute
long cyclelimit = MIN_PER_HR * hours; // # of cycles

cout << "Enter the average number of customers per hour: ";
double perhour; // average # of arrival per hour
cin >> perhour;
double min_per_cust; // average time between arrivals
min_per_cust = MIN_PER_HR / perhour;

Item temp; // new customer data
long turnaways = 0; // turned away by full queue
long customers = 0; // joined the queue
long served = 0; // served during the simulation
long sum_line = 0; // cumulative line length
int wait_time = 0; // time until autoteller is free
long line_wait = 0; // cumulative time in line


// running the simulation
for (int cycle = 0; cycle < cyclelimit; cycle++)
{
if (newcustomer(min_per_cust)) // have newcomer
{
if (line.isfull())
turnaways++;
else
{
customers++;
temp.set(cycle); // cycle = time of arrival
line.enqueue(temp); // add newcomer to line
}
}
if (wait_time <= 0 && !line.isempty())
{
line.dequeue (temp); // attend next customer
wait_time = temp.ptime(); // for wait_time minutes
line_wait += cycle - temp.when();
served++;
}
if (wait_time > 0)
wait_time--;
sum_line += line.queuecount();
}

// reporting results
if (customers > 0)
{
cout << "customers accepted: " << customers << endl;
cout << " customers served: " << served << endl;
cout << " turnaways: " << turnaways << endl;
cout << "average queue size: ";
cout.precision(2);
cout.setf(ios_base::fixed, ios_base::floatfield);
cout << (double) sum_line / cyclelimit << endl;
cout << " average wait time: "
<< (double) line_wait / served << " minutes\n";
}
else
cout << "No customers!\n";
cout << "Done!\n";
// cin.get();
// cin.get();
return 0;
}

// x = average time, in minutes, between customers
// return value is true if customer shows up this minute
bool newcustomer(double x)
{
return (std::rand() * x / RAND_MAX < 1);
}
51 changes: 51 additions & 0 deletions source/chapter12/placenew1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// placenew1.cpp -- new, placement new, no delete
#include <iostream>
#include <string>
#include <new>
using namespace std;
const int BUF = 512;

class JustTesting
{
private:
string words;
int number;
public:
JustTesting(const string & s = "Just Testing", int n = 0)
{words = s; number = n; cout << words << " constructed\n"; }
~JustTesting() { cout << words << " destroyed\n";}
void Show() const { cout << words << ", " << number << endl;}
};
int main()
{
char * buffer = new char[BUF]; // get a block of memory

JustTesting *pc1, *pc2;

pc1 = new (buffer) JustTesting; // place object in buffer
pc2 = new JustTesting("Heap1", 20); // place object on heap

cout << "Memory block addresses:\n" << "buffer: "
<< (void *) buffer << " heap: " << pc2 <<endl;
cout << "Memory contents:\n";
cout << pc1 << ": ";
pc1->Show();
cout << pc2 << ": ";
pc2->Show();

JustTesting *pc3, *pc4;
pc3 = new (buffer) JustTesting("Bad Idea", 6);
pc4 = new JustTesting("Heap2", 10);
cout << "Memory contents:\n";
cout << pc3 << ": ";
pc3->Show();
cout << pc4 << ": ";
pc4->Show();

delete pc2; // free Heap1
delete pc4; // free Heap2
delete [] buffer; // free buffer
cout << "Done\n";
// std::cin.get();
return 0;
}
56 changes: 56 additions & 0 deletions source/chapter12/placenew2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// placenew2.cpp -- new, placement new, no delete
#include <iostream>
#include <string>
#include <new>
using namespace std;
const int BUF = 512;

class JustTesting
{
private:
string words;
int number;
public:
JustTesting(const string & s = "Just Testing", int n = 0)
{words = s; number = n; cout << words << " constructed\n"; }
~JustTesting() { cout << words << " destroyed\n";}
void Show() const { cout << words << ", " << number << endl;}
};
int main()
{
char * buffer = new char[BUF]; // get a block of memory

JustTesting *pc1, *pc2;

pc1 = new (buffer) JustTesting; // place object in buffer
pc2 = new JustTesting("Heap1", 20); // place object on heap

cout << "Memory block addresses:\n" << "buffer: "
<< (void *) buffer << " heap: " << pc2 <<endl;
cout << "Memory contents:\n";
cout << pc1 << ": ";
pc1->Show();
cout << pc2 << ": ";
pc2->Show();

JustTesting *pc3, *pc4;
// fix placement new location
pc3 = new (buffer + sizeof (JustTesting))
JustTesting("Better Idea", 6);
pc4 = new JustTesting("Heap2", 10);

cout << "Memory contents:\n";
cout << pc3 << ": ";
pc3->Show();
cout << pc4 << ": ";
pc4->Show();

delete pc2; // free Heap1
delete pc4; // free Heap2
// explicitly destroy placement new objects
pc3->~JustTesting(); // destroy object pointed to by pc3
pc1->~JustTesting(); // destroy object pointed to by pc1
delete [] buffer; // free buffer
// std::cin.get();
return 0;
}
80 changes: 80 additions & 0 deletions source/chapter12/queue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// queue.cpp -- Queue and Customer methods
#include "queue.h"
#include <cstdlib> // (or stdlib.h) for rand()

// Queue methods
Queue::Queue(int qs) : qsize(qs)
{
front = rear = NULL; // or nullptr
items = 0;
}

Queue::~Queue()
{
Node * temp;
while (front != NULL) // while queue is not yet empty
{
temp = front; // save address of front item
front = front->next;// reset pointer to next item
delete temp; // delete former front
}
}

bool Queue::isempty() const
{
return items == 0;
}

bool Queue::isfull() const
{
return items == qsize;
}

int Queue::queuecount() const
{
return items;
}

// Add item to queue
bool Queue::enqueue(const Item & item)
{
if (isfull())
return false;
Node * add = new Node; // create node
// on failure, new throws std::bad_alloc exception
add->item = item; // set node pointers
add->next = NULL; // or nullptr;
items++;
if (front == NULL) // if queue is empty,
front = add; // place item at front
else
rear->next = add; // else place at rear
rear = add; // have rear point to new node
return true;
}

// Place front item into item variable and remove from queue
bool Queue::dequeue(Item & item)
{
if (front == NULL)
return false;
item = front->item; // set item to first item in queue
items--;
Node * temp = front; // save location of first item
front = front->next; // reset front to next item
delete temp; // delete former first item
if (items == 0)
rear = NULL;
return true;
}

// customer method

// when is the time at which the customer arrives
// the arrival time is set to when and the processing
// time set to a random value in the range 1 - 3
void Customer::set(long when)
{
processtime = std::rand() % 3 + 1;
arrive = when;
}
43 changes: 43 additions & 0 deletions source/chapter12/queue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// queue.h -- interface for a queue
#ifndef QUEUE_H_
#define QUEUE_H_
// This queue will contain Customer items
class Customer
{
private:
long arrive; // arrival time for customer
int processtime; // processing time for customer
public:
Customer() : arrive(0), processtime (0){}
void set(long when);
long when() const { return arrive; }
int ptime() const { return processtime; }
};

typedef Customer Item;

class Queue
{
private:
// class scope definitions
// Node is a nested structure definition local to this class
struct Node { Item item; struct Node * next;};
enum {Q_SIZE = 10};
// private class members
Node * front; // pointer to front of Queue
Node * rear; // pointer to rear of Queue
int items; // current number of items in Queue
const int qsize; // maximum number of items in Queue
// preemptive definitions to prevent public copying
Queue(const Queue & q) : qsize(0) { }
Queue & operator=(const Queue & q) { return *this;}
public:
Queue(int qs = Q_SIZE); // create queue with a qs limit
~Queue();
bool isempty() const;
bool isfull() const;
int queuecount() const;
bool enqueue(const Item &item); // add item to end
bool dequeue(Item &item); // remove item from front
};
#endif
Loading

0 comments on commit 88ff710

Please sign in to comment.