effective c++ item 25-29
item25:自定义swap函数
namespace std{template<typename T>void swap(T& a, T& b){T temp(a); // T要满足拷贝构造和拷贝赋值a = b;b = temp; }
}
1、Pimpl
2、自定义swap
item26:尽可能延后变量的定义
case 1:
temp j;
for(int i = 0; i < n; ++ i){j = func(i);
}case 2:
for(int i = 0; i < n; ++ i){temp j(i);
}
方案1调用了1次构造、1次析构和n次赋值
方案2调用了n次构造和n次析构
如果对程序效率不敏感,则方案2更好
item27:正确使用类型转换
1、c语言
(T) expression;
T(expression);
2、static_cast<T>(expression)
int x, y;
double d = static_cast<double> (x) / y;// 自定义变量
class Widget{
public:explicit Widget(int size);
};
void dosomework(const Widget& w);dosomework(Widget(15)); // function-style cast
dosomework(static_cast<Widget>(15)); // c++ style cast
#include <iostream>
using namespace std;class Base{
public:int a;
};class Derived:public Base{
public:double c;virtual void bar(){}
};void test(Base* base, Derived* derived){printf("%p, %p", base, derived); // 0x16fab2c58, 0x16fab2c50printf("%p\n", static_cast<Base*>(derived)); // 0x16fab2c58if(base == static_cast<Base*>(derived)){cout << "==" << endl; // == }else{cout << "!=" << endl;}
}int main(){Derived derived;test(&derived, &derived); return 0;
}
类中含有虚指针,8个字节,staticcast会移动类的指针
3、dynamic_cast<T> ()
在运行时类型转换,开销很大
4、const_cast<T>()
5、reinterpret_cast<T>()
item28:以引用的方式返回对象的成员
1、注意访问级别
以引用的方式返回的成员需要注意,因为可能会提高他的访问级别,导致被外部修改
class Point{
public:Point(int x, int y);void setX(int newVal);void setY(int newVal);
};struct RectData{Point ulhc; // upper left-hand cornerPoint lrhc; // lower right-hand corner
};class Rectangle{
public:Point& upperLeft() const { return pData->ulhc;} // const承诺不修改其中的成员变量Point& lowerRight() const{ return pData->lrhc;}
private:std::shared_ptr<RectData> pData;
};int main(){Point coord1(0, 0);Point coord2(100, 100);const Rectangle rec(coord1, coord2); // const表示成员状态不会变rec.upperLeft().setX(50); // 由于upperLeft返回的是引用,再调用setx就把成员状态更改了
}
为了防止上边那种意料之外的修改,我们可以这样更改:
class Rectangle{
public:const Point& upperLeft() const { return pData->ulhc;} // 增加const,返回一个const的指针const Point& lowerRight() const{ return pData->lrhc;}
private:std::shared_ptr<RectData> pData;
};
2、访问未定义内存
class GUIObject {...};
const Rectangle boundingBox(const GUIObject& obj);GUIObject *pgo; // make pgo point to some GUIObject
const Point *pUperLeft = &(boundingBox(*pgo).upperLeft()); // 返回一个临时变量的地址,在临时变量被释放后,操作pUperLeft会导致未定义行为// 应当用一个变量接收
auto bbox = boundingBox(*pgo);
const Point* pUpperLeft = &(bbox.uperLeft());
item29:努力写出异常安全的代码
class Menu{Mutex mutex;Image *bgImage;int imageChanges;
public:void changeBackground(std::istream& imgSrc);
};void Menu::changeBackground(std::istream& imgSrc){lock(&mutex);delete bgImage;++ imageChanges;bgImage = new Image(imgSrc); // 如果这一步没有new成功,会导致资源泄漏(锁未释放)、数据损坏(背景被删除、改变次数+1)unlock(&mutex);
}
异常安全(exception-safe functions):
- leak no resources
- dont allow data structures to become corrupted
上面的代码应该这样修改:
class Menu{Mutex mutex;std::shared_ptr<Image> bgImage;int imageChanges;
public:void changeBackground(std::istream& imgSrc);
};void Menu::changeBackground(std::istream& imgSrc){Lock ml(&mutex); // item14:acquire mutex and ensure its later release(RAII)bgImage.reset(new Image(imgSrc));++ imageChanges;
}