“The beautiful thing about learning is that no one can take it away from you.” - B.B. King
返回值有两种类型,以值的方式返回和以引用的方式返回。当返回的对象在该函数结束时被销毁,则必须以值的方式返回,如果是函数外的对象,则两种方式皆可。
如果要返回const类型的对象,那么引用返回时必须声明为const引用,值返回则无需如此,这是因为值返回时通过复制构造函数构造了一个临时对象,本质上已经不是原来那个对象了,而引用指向的仍是原来的对象。
当你确定要对返回的对象进行修改时,不要用const,典型的例子是重载赋值运算符和重载与cout一起使用的<<运算符。赋值运算符需要返回非const的类对象,以便于连续赋值时返回值能作为左值;<<运算符则要返回ostream&类对象,因为还需要改变cout来输出数据。
对于值引用的返回值,虽然在某些情况下可以作为左值被改动,但是由于本质上是构造了一个临时对象作为返回值,对临时对象改动值没有任何意义。
当你想要让编译器判定你是否不小心错误的改动了返回的临时对象时,可以将返回值设为const对象。
当new或delete一个类对象时,分为两个步骤:
事实上,new和delete运算符只完成第一部分的操作,我们现在使用的可以理解为将这两个操作封装在一起重载的new和delete。
定位new运算符可以在分配内存给对象时指定内存位置:
char* buffer = new char[BUF];//得到一块内存
JustTesting* pc1, * pc2;
pc1 = new (buffer)JustTesting;//将对象放入buffer指针指向的内存块
pc2 = new JustTesting("Heap1", 20);//将对象放在堆中
JustTesting* pc3, * pc4;
pc3 = new (buffer+sizeof(JustTesting))JustTesting("Better idea", 6);//避开pc1指向的对象占据的内存块,不然会发生错误
pc3->~JustTesting();//显式调用析构函数
pc1->~JustTesting();
delete pc2;//释放Heap1
delete[]buffer;//释放buffer的内存块,此时不会调用析构函数
队列是基本的数据结构之一,具有先进先出(FIFO)的属性,我们可以自己编写一个类来表示阉割版的队列,然后我们将使用这个队列来编写一个超市的收银系统,这个系统接待最先来的顾客并让他先走。
以下是队列的头文件声明,如果想自己试着写一下,可以直接跳过这段代码去看下面的说明:
class Queue
{
private:
//类范围定义
//Node是嵌套类结构定义
struct Node {
Item item;
struct Node* next;
};
enum { Q_SIZE = 10 };
Node* front;//指向队列最前端的指针
Node* rear;//指向队列最后端的指针
int items;//队列当中现有的对象数量
const int qsize;//队列之中最大的对象数量
//提前定义以阻止编译器构造默认复制构造函数和重载赋值运算符函数
Queue(const Queue& q) :qsize(0) {};
Queue& operator=(const Queue& q) {
return *this;
}
public:
Queue(int qs = Q_SIZE);//创建一个对象数量限制为qs的队列
~Queue();
bool isempty()const;
bool isfull()const;
int queuecount()const;
bool enqueue(const Item& item);//将item添加到队尾
bool dequeue(Item& item);//从队首删除Item
};
下面是一些重要函数的实现:
Queue::Queue(int qs) :qsize(qs)
{
front = rear = NULL;
items = 0;
}
Queue::~Queue()
{
Node* temp;
while (front != NULL)
{
temp = front;//保存队首的指针
front = front->next;//队首指针指向下一个对象
delete temp;//删除之前的队首
}
}
//将新的对象加入队列
bool Queue::enqueue(const Item& item)
{
if (isfull())
return false;
Node* add = new Node;//创建结点
add->item = item;//设置结点指针
add->next = NULL;//设置空指针
items++;
if (front == NULL)
front = add;
else
rear->next = add;
rear = add;
return true;
}
//将队首对象出列
bool Queue::dequeue(Item& item)
{
if (front == NULL)
return false;
item = front->item;
items--;
Node* temp = front;
front = front->next;
delete temp;
if (items == 0)rear = NULL;
return true;
}
构造函数名(函数参数):要被初始化的数据成员名(初始化该数据成员的参数名称){…};
这种方法也可以一次初始化多个数据成员,且可用范围不限于const成员,所有成员均可。
有些数据成员必须用这种方式进行初始化,不仅是const成员,还有引用数据成员,因为引用数据成员从一开始初始化就和对象绑定在一起且无法分开,所以它必须进行初始化。
编写出queue类后,我们编写customer(顾客)类,这个类作为queue类的测试item。下面是customer类的头文件:
class Customer
{
private:
long arrive;//顾客的到达时间
int processtime;//顾客的交易时间
public:
Customer()
{
arrive = processtime = 0;
}
void set(long when);
long when()const
{
return arrive;
}
int ptime()const
{
return processtime;
}
};
typedef Customer Item;
下面是set类的定义:
void Customer::set(long when)
{
processtime = std::rand() % 3 + 1;
arrive = when;
}
rand()函数生成一个随机数,对3取模生成一个[0,3)的数,再加一生成[1,4)的数,转换成int类型时舍去小数,于是processive是一个[1,3]的数。
下面是测试代码,这段代码有点难度,而且有些小漏洞,建议明白代码逻辑即可:
const int MIN_PER_HR = 60;
bool newcustomer(double x);
int main(void)
{
using namespace std;
srand(time(0));
cout << "Case Study:Bank of Heather Automatic Teller\n";
cout << "Enter maximum size of queue:";
int qs;
cin >> qs;
Queue line(qs);
cout << "Enter the number of simulation hours:";
int hours;
cin >> hours;
//模拟每分钟循环一次
long cyclelimit = MIN_PER_HR * hours;
cout << "Enter the average number of customers per hour:";
double perhour;//每小时平均到来的顾客的个数
cin >> perhour;
double min_per_cust;//两次顾客到达之间的平均时间间隙
min_per_cust = MIN_PER_HR / perhour;
Item temp;//新的顾客数据
long turnaways = 0;//因为队列排满被拒绝入队的顾客的数量
long customers = 0;//队列中的数量和服务过的顾客数量之和
long served = 0;//模拟期间服务的数量
long sum_line = 0;//累积的线路长度
int wait_time = 0;//正在处理的顾客业务的剩余处理时间
long line_wait = 0;//队列中所有顾客等待时间之和
//运行模拟
for (int cycle = 0; cycle < cyclelimit; cycle++)
{
//如果在这一次cycle中来了新顾客,那么尝试将新顾客入队
if (newcustomer(min_per_cust))
{
if (line.isfull())
turnaways++;
else
{
customers++;
temp.set(cycle);
line.enqueue(temp);
}
}
//如果上一个顾客已经交易完,那么将新顾客出队,处理新顾客的交易事宜
if (wait_time <= 0 && !line.isempty())
{
line.dequeue(temp);
wait_time = temp.ptime();
line_wait += cycle - temp.when();
served++;
}
//该顾客还未处理完
if (wait_time > 0)
wait_time--;
sum_line += line.queuecount();
}
//打印结果
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";
return 0;
}
//x是顾客平均到来的时间
//如果1分钟内出现了新顾客那么返回true
bool newcustomer(double x)
{
return (rand() * x / RAND_MAX < 1);
}
我是霜_哀,在算法之路上努力前行的一位萌新,感谢你的阅读!如果觉得好的话,可以关注一下,我会在将来带来更多更全面的知识讲解!