复制构造函数:
定义: 一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。
调用: 当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。
当将该类型的对象传递给函数或从函数返回该类型的对象是,将隐式使用复制构造函数。
析构函数:
定义: 析构函数可用于释放对象在构造或在对象的生命期中所获得的资源。
调用:当对象超出作用域或动态分配的对象被删除时,自动应用析构函数。
赋值操作符:可以通过制定不同类型的右操作数而重载。
复制构造函数、赋值操作符和析构函数 总称为复制控制。
复制构造函数:
两种初始化形式: 直接初始化; 复制初始化。
当用于类类型对象时:
直接初始化:直接调用与实参匹配的构造函数。
复制初始化:调用复制构造函数。 首先使用指定构造函数创建一个临时对象,然后用复制构造函数将临时对象复制到正在创建的对象。(两个步骤)
复制初始化条件: 支持复制的类型 且 构造函数非explicit
初始化容器元素: EX: 可以用表示容量的单个形参来初始化容器,这种容器的构造方式使用了默认的构造函数 和 复制构造函数。
vector<string> svec(5); // 首先 使用string 默认构造函数创建一个临时值 来初始化svec,然后使用复制构造函数将临时值复制到每个svec的每个元素。
构造函数与数组元素:
如果没有为类类型数组提供元素初始式,将用默认构造函数初始化每个对象。 如果使用常规的花括号括住 的数组初始化类表。 则使用复制构造函数来初始化。根据指定值创建适当类型的元素,然后用复制构造函数将该值复制到相应元素。(两步:构造-> 复制)
合成的复制构造函数: 执行逐个成员初始化,将新对象初始化为原对象的副本。 直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。
禁止复制:
为了防止复制,类必须显式声明其复制构造函数为private。如果连友元和成员中的复制也禁止,可以声明一个 private 复制构造函数但不对其定义。
注意:如果定义了复制构造函数,也必须定义默认构造函数。( 前面讲到的初始化容器列表或数组 可知 )
智能指针:
智能指针类将一个计数器与类指向的对象相关联。使用计数跟踪该类有多少个对象共享同一指针。
使用计数类: 定义一个单独的具体类用以封装使用计数和相关指针。
智能指针: 这些类在对象间
共享
同一基础值,从而提供了指针型行为。
以书上的两个习题作为笔记吧:
今天coding的时候发生了触发了这个assert
Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
经过百度,原因:释放了某些内存空间,但是程序里面还保留着对这个内存空间的应用或指针。
复制控制 的程序示例:
书上那个信息处理示例:
已注释
Message 类的头文件
#ifndef MESSAGE_H
#define MESSAGE_H
#include <string>
#include <set>
class Folder; // 同 Folder.h 的声明一样
class Message
{
public :
Message(const std::string &str=""):contents(str){}// 构造函数
Message(const Message&); //复制构造函数
Message& operator = (const Message&); // 重载赋值运算符
~Message();
void save(Folder &);
void remove(Folder &);
private:
std::string contents;
std::set<Folder*>folders;
void put_Msg_in_Folders(const std::set<Folder*>&);
void remove_Msg_form_Folders();
// [7/19/2012 LSFF]
void addFldr(Folder *);
void remFldr(Folder *);
};
#endif
Message 类的实现文件:
#include "Message.h"
#include "Folder.h"
#include <iostream>
#include <set>
Message::Message(const Message & m):
contents(m.contents), folders(m.folders)
{
put_Msg_in_Folders(folders);
}
void Message::put_Msg_in_Folders(const std::set<Folder *>& rhs) // 在folders 里面的每个Fold添加指向 msg的指针
{
for(std::set<Folder*>::const_iterator beg = rhs.begin();
beg!=rhs.end(); ++beg)
(*beg)->addMsg(this);
}
Message& Message::operator = (const Message &rhs)
{
if(&rhs!=this) // 赋值运算 会修改左操作数的值,所以应该判断右操作数跟左操作数是同一个
{
remove_Msg_form_Folders();
contents = rhs.contents;
folders = rhs.folders;
put_Msg_in_Folders(rhs.folders);
}
return *this;
}
void Message::remove_Msg_form_Folders()
{
for(std::set<Folder*>::const_iterator beg = folders.begin();
beg!=folders.end(); ++beg)
(*beg)->remMsg(this);
}
Message::~Message()
{
remove_Msg_form_Folders();
}
void Message::addFldr(Folder * nf)
{
folders.insert(nf);
}
void Message::remFldr(Folder *of)
{
if(folders.count(of)){
folders.erase(of);
of->remMsg(this);
}
}
void Message::save(Folder& nf)
{
addFldr(&nf);
nf.addMsg(this);
}
void Message::remove(Folder& of)
{
remFldr(&of);
of.remMsg(this);
}
Folder 类的头文件:
#ifndef FOLDER_H
#define FOLDER_H
#include <set> //下面有用到set容器,所以include 头文件
class Message; // 只是在文件中声明Message 类,不需 包括Message 头文件,声明class Message 即可
class Folder
{
public:
void addMsg( Message* );
void remMsg( Message* );
private:
std::set<Message*> msgs;
};
#endif
Folder类 的实现文件
#include "Folder.h" // 包含编译cpp 文件需要用到的头文件
#include "Message.h" // 有用到Message 类的函数调用,所以要包含该头文件
void Folder::addMsg(Message* nm)
{
msgs.insert(nm);
}
void Folder::remMsg(Message* om)
{
if(msgs.count(om))
msgs.erase(om);
}
main 文件
#include<iostream>
#include"Message.h"
#include"Folder.h"
using std::cout;
using std::cin;
using std::endl;
int main() // 下面的用例是随便弄的,调试的时候监视变化即可。
{
Folder fldr1;
Folder fldr2;
Message newMsg1("helloword");
Message newMsg2("byeword");
newMsg1.save(fldr1);d
newMsg1.save(fldr2);
newMsg2=newMsg1;
return 0;
}
智能指针的code 实例:
(已注释)
#ifndef U_PTR_H // 智能指针类的头文件
#define U_PTR_H
class HasPtr;
#include <iostream>
using std::cout;
using std::endl;
class U_Ptr
{
friend class HasPtr; // 把定义智能指针的类,即该类里面有指向智能指针类的指针成员。把该类设为友元。即能在该类里面直接访问智能指针
int *ip; // 真正指向 对象间共享的数据的 指针
size_t use; // 目前指向本对象的指针数。
// 某个智能指针类的对象在 "包含它的类" 定义某个对象时 也就定义了,
//而"包含它的类" 只是存储此对象的指针。 在这个“包含它的类" 的对象
//发生复制、赋值时 就通过其存储的指针改变 这些智能对象的指针数
U_Ptr(int *p): ip(p), use(1){} // 定义的时候只有一个指针指向它
~U_Ptr(){ cout<<"delete:"<<*ip<<endl;delete ip; }
};
#endif
包含U_Ptr 类的 HasPtr 类 头文件
#ifndef HASPTR_H
#define HASPTR_H
#include "U_Ptr.h" // 在这里不能只声明 class U_Ptr ,因为下面有函数用到U_Ptr 类里面的成员。其实可以通过只声明这些函数而在对应cpp文件中再实现
class U_Ptr; // 那样就可以include "U_Ptr.h" 头文件,但是inline函数一定要在头文件中实现。 所以二者应该不能兼得。
// 通过只声明 class U_Ptr ,只能定义 该类指针、引用,或作为函数参数,不能定义该类。
class HasPtr
{
public :
HasPtr(int *ip, int i): ptr(new U_Ptr(ip)), val(i){}; // new 函数返回的是 申请内存的地址,谨记谨记。赋给指针
HasPtr (const HasPtr& hp):ptr(hp.ptr), val(hp.val){ ++ptr->use; }
HasPtr & operator = (const HasPtr&) ;
~HasPtr ()
{
if(--ptr->use == 0) delete ptr;
}
private:
int val;
U_Ptr *ptr;
};
#endif
HasPtr 类 实现文件
#include<iostream>
#include "HasPtr.h"
#include "U_Ptr.h"//
HasPtr & HasPtr::operator=(const HasPtr & rval)
{
++rval.ptr->use; // 先加 右操作数 的use 数,再减去 左操作数的 use数,是为了防止 左右操作数是同一个的情况。
if(--ptr->use==0)
delete ptr;
ptr=rval.ptr;
val=rval.val;
return *this;
}
main 文件:
#include <iostream>
#include "HasPtr.h"
int main()
{
HasPtr hp1(new int(4),3);
HasPtr hp2(hp1);
HasPtr hp3(new int(5),4);
hp1=hp3;
hp2=hp3;
return 0;
}