//13.3假定Point为类类型,该类类型有一个复制构造函数,指出下面程序段中每一个试用了复制构造函数的地方。
Point global; //默认构造函数
Point foo_bar(Point arg) //复制构造函数
{
Pointlocal = arg; //复制构造函数
Point*heap =new Point(global); //复制构造函数
*heap= local;
Pointpa[ 4 ] = { loca, *heap }; //复制构造函数,复制构造函数,默认构造函数,默认构造函数
return *heap; //复制构造函数
}
//下边是测试程序
#include<iostream>
#include<stdlib.h>
usingnamespace std;
classPoint
{
public:
int T;
public:
staticint total;
Point(int t=0);
Point(const Point&a){T=a.T+1; cout<<total++<<"复制构造函数"<<T<<endl;}
~Point(){cout<<total++<<"析构函数"<<T<<endl;}
};
intPoint::total=1;
Point::Point(intt):T(t){cout<<total++<<"构造函数"<<T<<endl;}
Point global(1);
Point foo_bar(Point arg) //这里将会调用复制构造函数生成对象arg
{
::system("pause");
Pointlocal = arg; //这里先用默认构造函数
::system("pause");
Point*heap =new Point(global);
::system("pause");
*heap= local;
::system("pause");
Pointpa[ 4 ] = { local, *heap };
::system("pause");
return *heap;
}
intmain()
{
Pointarg(2);
::system("pause");
foo_bar(arg);
return 0;
}
//13.4对于如下的类的简单定义,编写一个复制构造函数复制所有成员,复制pstring指向的对象而不是复制指针
#include<iostream>
#include<string>
structNoName{
NoName(std::stringstr ="abc"):pstring(new std::string(str)), i(0), d(0){}
NoName(const NoName& T);
private:
std::string*pstring;
int i;
double d;
};
NoName::NoName(constNoName& T)
{
i= T.i;
d= T.d;
pstring=new std::string(*T.pstring);
std::cout<<*pstring<<std::endl;
}
intmain()
{
NoNameA;
NoNameB(A);
return 0;
}
//13.5哪个类定义可能需要一个复制构造函数
//a.包含四个float成员的Point3w类
//b.Matrix类,其中,实际矩阵在构造函数中动态分配,在析构函数中删除
//c.Payroll类,在这个类中为每个对象提供唯一的ID
//d.Word类,包含一个string和一个以行列位置对为元素的vector
//b,c
//13.6复制构造函数的形参并不限制为const,但必须是一个引用。解释这个限制的基本原理。
//如果不是引用,会导致递归调用复制构造函数。
//13.7类何时需要定义赋值操作符
//也就是和需要复制构造函数一样的情况吧,一般就是指针的时候啊什么的
//13.8对于习题.5中列出的每个类型,指出类是否需要赋值操作符
//同复制构造函数
//13.9习题.4中包括NoName类的简单定义。确定这个类是否需要赋值操作符。如果需要,实现它。
structNoName{
NoName(std::stringstr ="abc"):pstring(new std::string(str)), i(0), d(0){}
NoName&operator=(constNoName& rhs);
private:
std::string*pstring;
int i;
double d;
};
NoName& NoName::operator =(constNoName &rhs)
{
i= rhs.i;
d= rhs.d;
pstring=new std::string(*rhs.pstring);
return *this;
}
//13.10定义一个Employee类,包含雇员名字和一个唯一的雇员标识。
//为该类定义默认构造函数和参数为表示雇员名字的string的构造函数。
//如果该类需要赋值构造函数或复制操作符,实现这些函数。
classEmployee{
private:
std::stringname;
std::stringID;
staticint counter;
public:
Employee():name("NoName"), ID(0) { counter++;}
Employee(std::string_name): name(_name), ID(counter++){}
Employee(const Employee& other):name(other.name),ID(counter++){}
Employee&operator=(constEmployee& rhs);
};
intEmployee::counter = 1;
Employee& Employee::operator=(constEmployee &T)
{
name= T.name;
return *this;
}
//13.11什么是析构函数,合成析构函数有什么用,什么时候会合成析构函数,什么时候一个类必须定义自己的析构函数
//析构函数是特殊的成员函数,其名字是在类名字前加上一个代字号(~)。
//该函数没有返回值和形参,用于对象超出作用域或需要删除对象时来清除对象。
//合成析构函数的作用:按对象创建时的逆序(即成员在类中声明次序的逆序)撤销每个非static成员。
//对于类类型的成员,合成析构函数调用该成员的析构函数来撤销对象。
//编译器会为每个类合成析构函数。如果有些工作(如释放资源、执行特定操作等)需要析构函数完成,一个类就必须定义自己的析构函数。
//13.12确定在习题.4中概略定义的NoName类是否需要析构函数,如果需要实现它
NoName::~NoName()
{
delete pstring;
}
//13.13确定在习题.10中定义的Employee类是否需要析构函数,如果需要实现它
//不需要
//13.14理解复制控制成员和构造函数的一个良好方式是定义一个简单类,该类具有这些成员
//每个成员打印自己的名字
#include<iostream>
#include<vector>
structExmpl{
Exmpl(){ std::cout <<"Exmpl()"<< std::endl; }
Exmpl(const Exmpl&)
{std::cout <<"Exmpl(constExmpl&)" << std::endl; }
Exmpl&operator= (constExmpl& )
{std::cou <<"Exmpl& operator= (constExmpl&)" <<std::endl; }
~Exmpl(){ std::cout <<"~Exmpl()"<< std::endl; }
};
//编写一个像Exmpl这样的类,给出复制控制成员和其他构造函数,然后写一个程序,
//用不同方式使用Exmpl类型的对象:作为非引用形参和引用形参传递,动态分配,放在容器中,等等
//研究何时执行哪个构造哈数和复制控制成员,可以帮助你荣偶会贯通的理解这些概念
//程序有点长。。。反正也都理解了,就不写了,毕竟情况太多都打出来也有些麻烦
//13.15下面的代码段中发生了多少次析构函数的调用
voidfcn(const Sales_item *trans, Sales_item accum)
{
Sales_itemitem1(*trans), item2(accum);
if (!item1.same_isbn(item2))return;
if (item1.avg_price() <= 99)return;
elseif(item2.avg_price() <= 99)return;
}
//accum item1 item2
//13.16编写本节中描述的Message类
//13.17为Message类增加与Folder的addMsg和remMsg操作类似的函数。这些函数可以命名为addFldr和remFldr,
//应接受一个指向Folder的指针并将该指针插入到folders。这些函数可为private的,因为它们将仅在Message类的实现中使用
//13.18编写相应的Folder类,该类应保存一个set<Message*>,包含指向Message的元素。
//13.19在Message类中增加save和remove操作。这些操作应接受一个Folder,
//并将该Folder加入到指向这个Message的Folder集中(或从其中删除该Folder)。该操作还必须更新Folder以反映它指向该Message
//这可以通过调用addMsg或remMsg完成。
#include<set>
#include<string>
classFolder;
//Message类定义
classMessage
{
public:
Message(const std::string &str =""):
content(str){};
Message(const Message&);
Message&operator= (constMessage&);
~Message();
void save(Folder&);
void remove(Folder&);
void addFldr(Folder*);
void remFldr(Folder*);
private:
std::stringcontent;
std::set<Folder*>folders;
void put_Msg_in_Folders();
void remove_Msg_from_Folders();
};
//Folder类定义
classFolder
{
public:
Folder(){}
Folder(const Folder&);
Folder&operator= (constFolder&);
~Folder();
void save(Message&);
void remove(Message&);
void addMsg(Message*);
void remMsg(Message*);
private:
std::set<Message*>messages;
void put_Fldr_in_Messages();
void remove_Fldr_from_Messages();
};
//Message函数定义
//复制构造函数
Message::Message(const Message& other):
content(other.content),folders(other.folders)
{
put_Msg_in_Folders();
}
//赋值函数
Message& Message::operator=(constMessage& rhs)
{
if(&rhs !=this)
{
remove_Msg_from_Folders();
content= rhs.content;
folders= rhs.folders;
put_Msg_in_Folders();
}
return *this;
}
//析构函数
Message::~Message()
{
remove_Msg_from_Folders();
}
//put_Msg_in_Folders
voidMessage::put_Msg_in_Folders()
{
for(std::set<Folder*>::const_iteratorcur(folders.begin());
cur!= folders.end(); cur++)
(*cur)->addMsg(this);
}
//remove_Msg_from_Folders
voidMessage::remove_Msg_from_Folders()
{
for(std::set<Folder*>::const_iteratorbeg(folders.begin());
beg!= folders.end(); beg++)
(*beg)->remMsg(this);
}
voidMessage::addFldr(Folder* fldr)//这里不能用const,因为和set容器中的数据类型是关联的
{
folders.insert(fldr);
}
voidMessage::remFldr(Folder* fldr)
{
folders.erase(fldr);
}
voidMessage::save(Folder& fldr)
{
addFldr(&fldr);
fldr.addMsg(this);
}
voidMessage::remove(Folder& fldr)
{
remFldr(&fldr);
fldr.remMsg(this);
}
//Folder函数定义
//复制构造函数
Folder::Folder(constFolder& other):
messages(other.messages)
{
put_Fldr_in_Messages();
}
//赋值函数
Folder& Folder::operator= (constFolder& rhs)
{
if(&rhs !=this)
{
Remove_Fldr_from_Messages();
messages= rhs.messages;
put_Fldr_in_Messages();
}
return *this;
}
//析构函数
Folder::~Folder()
{
remove_Fldr_from_Messages();
}
voidFolder::put_Fldr_in_Messages()
{
for(std::set<Message*>::const_iteratorbeg(messages.begin());
beg!= messages.end(); beg++)
(*beg)->addFldr(this);
}
voidFolder::remove_Fldr_from_Messages()
{
for(std::set<Message*>::const_iteratorbeg(messages.begin());
beg!= messages.end(); beg++)
(*beg)->remFldr(this);
}
voidFolder::addMsg(Message* msg)
{
messages.insert(msg);
}
voidFolder::remMsg(Message* msg)
{
messages.erase(msg);
}
voidFolder::save(Message& msg)
{
addMsg(&msg);
msg.addFldr(this);
}
voidFolder::remove(Message& msg)
{
remMsg(&msg);
msg.remFldr(this);
}
//13.20对于HasPtr类的原始版本(依赖于复制控制的默认定义),描述下面代码中会发生什么
inti = 42;
HasPtr p1(&i, 42);
HasPtr p2 = p1;
cout << p2.get_ptr_val() <<endl;
p1.set_ptr_val(0);
cout << p1.get_ptr_val()<<endl;
//值会变呗。。
//13.21如果给HasPtr类添加一个析构函数,用来删除指针成员,会发生什么
//导致指针悬挂吧
//13.22什么是使用计数
/*使用计数是复制控制成员中使用的编程计数。将一个计数器与类指向的对象相关联,
用于跟踪该类有多少个对象共享同一指针,创建一个单独类指向共享对象并管理使用计数。
由构造函数设置共享对象的状态并将使用计数置为.每当由复制构造函数或赋值操作符生成一个新副本时,
使用计数加.由析构函数撤销对象或作为赋值操作符的左操作数撤销对象时,使用计数减.
赋值操作符和析构函数检查使用计数是否已减至,如果是,则撤销对象。
*/
//13.23什么是智能指针,智能指针类如何与实现普通指针行为的类相区别
/*智能指针是一个行为类似指针但也提供其他功能的类。
智能指针类与实现普通指针行为的类的区别在于:智能指针通常接受指向动态分配对象的指针并负责删除该对象。
用户分配对象,但由智能指针类删除它,因此只能指针类需要实现复制控制成员来管理指向共享对象的指针。
只有在撤销了指向共享对象的最后一个智能指针后,才能删除该对象对象。使用计数是实现只能指针类最常用的方式。
*/
//13.24实现你自己的使用计数式HasPtr类的版本
classHasPtr;
classU_Ptr{
friendclass HasPtr;
private:
int *ptr;
int num;
U_Ptr(int *p):ptr(p),num(1){}
~U_Ptr(){delete ptr;}
};
classHasPtr{
public:
HasPtr(int *_p,int _val): p(new U_Ptr(_p)), val(_val){}
HasPtr(const HasPtr& obj): p(obj.p), val(obj.val)
{p->num++; }
HasPtr&operator= (constHasPtr&);
~HasPtr(){if(--p->num == 0)deletep; }
private:
U_Ptr*p;
int val;
};
HasPtr& HasPtr::operator= (constHasPtr& obj)
{
++obj.p->num;
if(--p->num)
delete p;
p= obj.p;
val= obj.val;
return *this;
}
//13.25什么是值类型
/*所谓值类型,是指具有值语义的类,其特征为:对该类对象进行复制时,会得到一个不同的新副本,对副本所做的改变不会影响原有的对象。*/
//13.26实现你自己的值型HasPtr类版本
classHasPtr{
public:
HasPtr(constint &p,int i):
ptr(newint(p)), val(i){}
HasPtr(const HasPtr& obj):
ptr(newint(*obj.ptr)),val(obj.val){}
HasPtr&operator= (constHasPtr&);
~HasPtr(){delete ptr;}
private:
int *ptr;
int val;
};
//这里注意,赋值操作符不需要再重分配空间
HasPtr& HasPtr::operator= (constHasPtr& obj)
{
*ptr= *obj.ptr;
val= obj.val;
return *this;
}
//13.27值型HasPtr类定义了所有复制控制成员。描述将会发生什么,如果该类:
//a,定义了复制构造函数和析构函数但没有定义赋值操作符
//b,定义了复制构造函数和赋值操作符但没有定义析构函数
//c,定义了析构函数但没有定义复制构造函数和赋值操作符
//a,赋值时指针指向相同
//b,内存泄露
//c,指针悬挂