拷贝函数的正常使用
来源:互联网 发布:剑灵蒂法捏脸数据图 编辑:程序博客网 时间:2024/05/02 06:05
C++面向对象的程序语言,到处存在继承体系:整理一下使用构造函数与析构函数注意事项:
Item1: 编译器可以隐式自动生成的函数包括四个:
defatult constructor:默认构造函数
copy constructor:拷贝构造函数
copy assignment operator:拷贝赋值函数
destructor:析构函数
编译器可以自动生成,并不代表一定会生成,只有在用户未手动声明对应函数时,编译器可以自动生成相应的函数。
classBase{}; // 四个编译器全部自动生成
classBase
{
Base();
}; // 编译器生成除default constructor外的三个函数
Item2: 不想使用编译器自动生成的函数应该明确告诉编译器
如果想要实现object禁止拷贝功能:
classBase // F1
{
public:
Base(){}
~Base(){}
private:
Base(constBase&);
Base&operator=(constBase&);
};
classBase // F2
{
public:
Base(){}
~Base(){}
Base(constBase&)=delete;
Base&operator=(constBase&)=delete;
};
classBase // F3
{
public:
Base(){}
~Base(){}
private:
Base(constBase&)=delete;
Base&operator=(constBase&)=delete;
}
Q1:为什么拷贝函数设置为私有函数?
A1:因为私有函数外部无法访问,可以达到无法创建object的目的。
Q2:为什么拷贝函数只有声明而没有实现?
A2:因为万事都有例外,友元函数可以冲破private,所以只有声明没有实现,
从而可以达到调用拷贝函数提醒报错的目的。
Q3:除了私有化拷贝函数还有别的方法实现吗?
A3:有,利用C++11的特性,使用keyword:delete。
Item3: member data initializtion of object(member initialization list)
Baseclass的初始化要在Derived class前初始化,成员变量的初始化要在初始化列表中按照声明顺序进行初始化。
因为只调用一次copy constructor function效率比先调用一次defaultconstructor function后再调用一次copy assignment operator效率要高的多。所有建议使用member initializtion list对member data 进行初始化。
classPeople
{
public:
People(conststd::stringname,constintage);
virtual~People();
private:
std::stringm_str_name;
intm_int_age;
};
People::People(conststd::stringname,constintage)
:m_str_name(name),m_int_age(age){}
People::~People(){}
classHoney:publicPeople
{
public:
Honey(conststd::stringname,constintage);
~Honey();
};
Honey::Honey(conststd::stringname,constintage)
:People(name,age){}
Honey::~Honey(){}
Item4: virtual destructor的正确使用
如果一个class准备作为base class 使用,将destructor设置为virtual destructor是一个受欢迎的行为。因为在创建derived class时候,可以自动销毁base class资源。
如果一个class不准备作为base class使用,将destructor设置为virtual destructor是一不好的想法。因为虚函数意味着使用更多的内存。
Item5: copy all data(local data and base data)
拷贝一个对象的所有数据包括:本地所有数据成员,以及调用基类中适当的拷贝函数。
如果为一个类增加了一个成员,则就应该更新所有拷贝函数。确保所有数据成员都可以正确拷贝。
不要企图用一个拷贝函数调用另一个拷贝函数完成相应的功能。重复的代码可以提取出来作为第三个函数(private:init();)
classPeople
{
public:
People()=default;
virtual~People(){}
};
classHoney:publicPeople
{
public:
Honey(conststd::stringname,constintage);
~Honey();
private:
std::stringm_str_name;
intm_int_age;
};
Honey::Honey(conststd::stringname,constintage)
:People(),m_str_name(name),m_int_age(age){}
Honey::~Honey(){}
Item6: construtors and destrcutors不应该调用virtual function
虚函数的实现依赖于vptr,vptr存在于base class 中。
不要在constructor和destructor中调用virtual function,因为不会出现多态现象,为什么?因为在constructor中调用virtual function时,derived object还没有创建完成,只会出现base class中的行为。在析构函数中调用virtual function当寻找vptr时derived class已经被析构完成,则仍为base class中的行为。
如果拷贝函数与构造函数存在重复的代码可以将公共的代码提取出来放入第三个函数(private function)。
classPeople
{
public:
People(){init("people",30);}
virtual~People(){}
private:
virtualvoidinit(std::stringname,constintage)=0;
virtualvoiddestroy()=0;
};
classHoney:publicPeople
{
public:
Honey(){init("honey",28);}
~Honey();
private:
std::stringm_str_name;
intm_int_age;
voidinit(std::stringname,constintage)override;
voiddestroy()override;
};
voidHoney::init(std::stringname,constintage)
{
m_str_name=name;
m_int_age=age;
}
voidHoney::destroy(){}
Item7: exceptions不应该出现有constructor中
对于异常的结果处理分为三种:
第一种:抑制异常使用程序继续运行。
第二种:终止程序。
第三种:提供常规异常处理函数(需要用户参与时)。
destructor应该永不引发异常,因为如果在destructor中引发异常意味着内存泄漏。如果调用可能会发生异常的函数,则destructor应该捕获所有异常,然后抑制异常或者终止程序。一般来说,抑制异常比程序提前终止或产生未定义行为更可取。因为一个应用程序必须可以保证抑制异常后还能正常可靠的运行。
建议使用智能指针代替常规指针,使用vector代替数组。
Item8: assignment operator should return refernce to*this
assignment operator是一个右结合运算符
int x, y, z;
x = (y = (z = 15));
classPeople
{
public:
People()=default;
People&operator=(constPeople&rhs);
private:
std::stringm_str_name;
};
People&People::operator=(constPeople&rhs)
{
m_str_name=rhs.m_str_name;
return*this;
}
Item9:在asiignment中处理assignment to self
assignment to self是由于别名造成的(多个指针指向同一块内存,多个引用与同一个变量进行绑定)。
classPeople
{
public:
People()=default;
People&operator=(constPeople&rhs);
private:
std::stringm_str_name;
};
People&People::operator=(constPeople&rhs)
{
if(this==&rhs) return*this; //自赋值安全
People*p=this; //异常安全
this=newPeople(rhs);
deletep;
return*this;
}
如果可以做到上面九点,我想应该可以安全高效的处理好对象之间的拷贝关系了。
参考effective c++后的个人意见,仅供参考,如果有什么忽略的或者好的意见,请大家不吝赐教。
(ps :多年工作以来,一直都看别人的文章,以后本人会整理更多想法与大家分享:大家可以一直讨论,一起学习,一直进步。邮箱:wangqing10520@sina.com)。
- 拷贝函数的正常使用
- 拷贝构造函数的使用
- 拷贝构造函数使用const的原因
- 拷贝构造函数的使用时机
- C++之析构函数/拷贝构造函数/拷贝赋值函数的概念和使用
- C++构造函数和拷贝构造函数等的使用
- VS2015不能正常使用scanf函数的解决办法
- 拷贝文件的函数!
- 文件的拷贝函数
- 构造函数的拷贝
- 函数的拷贝
- 函数指针的正常使用,函数指针作为函数参数使用,函数指针作为函数参数使用
- 使用缺省的拷贝构造函数带来的危险性
- 拷贝构造函数的参数为什么必须使用引用类型
- 拷贝构造函数的参数为什么必须使用引用类型.
- 拷贝构造函数的参数为什么必须使用引用类型
- 拷贝构造函数的参数为什么必须使用引用类型
- 拷贝构造函数的参数为什么必须使用引用类型
- 用蓝牙耳机播放键激活PC小娜
- Mac上Homebrew的使用
- python 查看帮助
- hadoop fs -mkdir 报错 No such file or directory
- Mongodb报错及解决办法集合
- 拷贝函数的正常使用
- 收集知识点--微信小程序(以后或许用到)--持续更新
- 类加载时候的构造器加载顺序
- nginx配置Let's Encrypt免费SSL证书
- 安卓毕业设计代做 Android毕业设计定制
- activity启动动画,从右边进来,左边出去
- 解决 error: Unable to find vcvarsall.bat
- zoj1825
- svnserve –-version不是内部命令