effective c++(二)

当为派生类撰写复制构造函数时,必须很小心地也复制其基类成分,让派生类的复制构造函数调用相应的基类函数

class PriorityCustomer:public Customer
{
 public:
 PriorityCustomer(const PriorityCustomer &rhs);
 PriorityCustomer& operator=(const PriorityCusstomer& rhs);
 ...
}
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
:Customer(rhs),
 priority(rhs.priority)
{
 ...
}
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
 Customer::operator=(rhs);
 ...
}

令copy assignment操作符调用复制构造函数是不合理的,因为这就像试图构造一个已经存在的对象。同样让复制构造函数调用copy assignment操作符同样无意义,构造函数用来初始化新对象,而assignment操作符只施行与已初始化对象身上,对一个尚未构造好的对象赋值,就像在一个尚未初始化的对象身上做只对已初始化对象才有意义的事一样。

所谓资源就是,一旦用了它,将来必须还给系统,把资源放进对象内,便可依赖C++的析构函数自动调用机制确保资源被释放。记住资源取得时机便是初始化时机

auto_ptr是个类指针对象,也是所谓的智能指针,其析构函数自动对其所指对象调用delete,由于auto_ptr被销毁时自动删除它所指之物,所以一定要注意别让多个auto_ptr指向同一个对象,同时它有一个不寻常的性质,若通过复制构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权。

auto_ptr和trl::shared_ptr两者都有其析构函数内做delete而不是delete[]动作,因此对于用动态创建的数组让它们指向是错误的

trl::shared_ptr允许指定所谓的删除器,那是一个函数或函数对象,当引用次数为0时便被调用,删除器对于shared_ptr的构造函数而言是可有可无的第二参数。

复制资源管理对象时,进行的是深度拷贝,对于资源管理对象的复制行为通常为抑制复制和施行引用计数法(shared_ptr)

shared_ptr和auto_ptr都提供一个get成员函数,用来执行显式转换,也就是它会返回智能指针内部的原始指针(的复件)

std::trl::shared_ptr<Investment>pInv(createInvestment());
int daysHeld(const Investment* pi);
int days=daysHeld(pInv.get());//将pInv内的原始指针传给daysHeld

就像几乎所有智能指针一样,shard_ptr和auto_ptr也重载了->和*,它们允许隐式转换至底部原始指针,就是可以通过这两个操作符取访问类内部资源。

是否提供一个显式转换函数还是一个隐式转换函数主要取决于类被设计执行的特定工作,以及它的使用情况。

当你对这一个指针使用delete,唯一能够让delete知道内存中是否存在一个数组大小记录的方法是由你来告诉它。所以当对一个单一对象使用delete[]或对一个数组使用delete,都会产生未定义的行为!尽量不要对数组形式做typedefs动作!容易出错!

扫描二维码关注公众号,回复: 1931160 查看本文章

shared_ptr构造函数需要一个原始指针,但该构造函数是个explicit构造函数,无法进行隐式转换,所以如下无法通过编译

void processWidget(std::trl::shared_otr<Widget> pw,int priority);
processWidget(new Widget,priority());//无法通过编译

这样就可以

processWidget(std::trl::shared_ptr<Widget>(new Widget),priority());

编译器产出一个processWidget调用码之前,必须首先核算即将被传递的各个实参,因此编译器必须创建代码,做下面三件事,调用priority,执行new Widget,调用trl::shared_ptr构造函数,但是这三个事情的执行顺序是不确定的,随机的,由编译器决定的,因此万一priority调用异常导致new Widget返回的指针将遗失,因为在资源被创建和资源被转换为资源管理对象两个时间点之间有可能发生异常干扰。因此要以独立语句将New对象存储于智能指针内。因此应该:

std::trl::shared_ptr<Widget>pw(new Widget)
processWidget(pw,priority());

任何接口如果要求客服必须记得做某些事情,就是有着不正确使用的倾向

shared_ptr构造函数坚持其第一参数必须是个指针。通过转换的也可以接受

让接口容易被正确使用,不易被误用,促进正确使用的办法包括接口的一致性,以及与内置类型的行为兼容,阻止误用的办法包括建立新类型,限制类型上的操作,束缚对象值,以及消除客户的资源管理责任。

设计高效的类必须要求面对一下的提问:

  • 新类型的对象应该如何被创建和销毁-影响到你的类的构造函数和析构函数以及内存分配函数和释放函数
  • 对象的初始化和对象的赋值该有声明样的差别-决定你的构造函数和赋值操作符的行为,以及其间的差异
  • 新的类型的对象如果被passed by value,意味着什么,copy函数用来定义一个类的pass-by-value该如何实现
  • 什么是新类型的合法值-决定了错误检查工作,以及函数抛出的异常等等
  • 你的新类型需要配合某个继承图系-注意虚函数的影响
  • 新类型需要什么样的转换-为适当的转换提供转换函数
  • 什么样的操作符和函数对此新类型而言是合理的-决定声明哪些函数,哪些为成员函数
  • 什么样的标准函数应该驳回-那些正是你必须声明为私有者
  • 谁该取用新类型的成员-决定成员变量哪个为公有哪个为私有,决定哪些类型或函数是朋友
  • 什么是新的类型的未声明接口-对效率,异常安全性以及资源运用提供何种保证新的类有那么一般化-这种情况决定是否是应该定义一个新的class template
  • 你真的需要一个新类型吗-如果只是定义新的派生类以便为既有的类添加技能,那么说不定单纯定义一或多个函数更好

以pass by reference的传递方式效率高得多,没有任何构造函数或析构函数被调用,因为没有任何新对象被创建。

pass by value容易造成参数切割,考虑如下代码,参悟W会被构造成为一个window对象,它是by value,所有windowWithScropBars对象的所有特化信息都会被切除,在函数内不论传递过来的对象原本是什么类型,参数W就像一个WINDOW对象,因此在函数内调用的虚函数也都是window的函数

class Window{
 ...
}
class WindowWithScrollBars:public Window{
...
}
void printNameAndDisplay(Window w)
{
 ...
}
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);

解决方法是以by reference to-const的方式传递w

void printNameAndDisplay(const Window& w)
{
 ...
}

如果你有个独享属于内置类型,pass by value往往比pass by reference的效率高些,对于内置类型和stl的迭代器和函数对象,选择为pass by value是值得的

任何时候看到一个reference声明式,你都应该立刻问自己,它的另一个名称是什么,因为它一定是某物的另一个名称。必须返回对象时,千万别妄想返回其reference,那一定是糟糕的错误!

其实只有两种访问权限,private(提供封装)和其他(不提供封装),保护成员并不比公有成员更具有封装性

猜你喜欢

转载自blog.csdn.net/weixin_38893389/article/details/79487034