s1 赋值给 s2 时,有如下两种方案:
s2;【也就是所谓的 eager evaluate(急式评估),即立即求值】s2 只是指向 s1,当需要对 s2 修改时,才真正复制 s1 中的内容。【缓式评估】s2 的任何读操作,只需要 s1 的值即可;然而,一旦需要对 s2 的值进行写操作,就不能再做任何拖延,必须为 s2 做一份真实副本并进行写操作。String s1="Hello World!";
String s2=s1;
cout<<s2[0]; // 读操作
cin>>s2[1]; // 写操作
class LargeObject{
public:
LargeObject(ObjectID id); // 根据 id 识别对象
const string&field1()const; // 不同字段
int field2()const;
double field3()const;
const string& field4()const;
const string& field5()const;
...
private:
ObjectID oid;
// 对象内的每个字段都是指针,指向必要的数据
mutable string *field1Vaule;// 注意使用了mutable修饰符
mutable int *field2Value;
mutable double *field3Value;
mutable string *field4Value;
...
}
// 各参数值初始化为 0,即空指针 NULL。
LargeObject::LargeObject(Object id):oid(id),field1Value(0),field2Value(0),field3Value(3)...{}
const string& LargeObject::field1()const{
// 判断指针是否为空,如果为空,则要进行读取
// NULL初始值表示该字段未被读入,需要先从数据库读入
// 将字段指针声明为mutable,可以保证字段指针可以**在任何时候都能被更改**以指向实际数据(即使是在const成员函数内也一样)
if(field1Value==0){
read the data from field 1 from the database and make field1Vaule point to it;
}
return *field1Value;
}
const string& LargeObject::field1()const{
LargeObject* const fakeThis=const_cast<LargeObject*const>(this);
if(field1Value==0){
fakeThis->field1Value=the proper data from the database;
}
return *field1Value;
}
// 矩阵运算的场景如下:
template<class T>
class Matrix{...}
Matrix<int> m1(1000,1000);
Matrix<int> m2(1000,1000);
// ...
Matrix<int>m3=m1+m2;// 将m3定位为m1和m2的和
m3 中,用于标记 m3 是 m1 和 m2 的总和:【这个数据结构可能只由两个指针和一个 enum 组成:前者指向 m1 和 m2,后者用来表示运算动作】// 假设接下来对m3使用之前,进行了更新:
Matrix m4(1000,1000);
m3=m4*m1;// 可以直接将m3定位为m4和m1的乘积
// 但如果读m3的某个位置进行读取,就不能再拖延:
cout<<m3[4];
// 在此时,也只需要计算m3第四行的值,不需要计算所有的值
// 但是有时候缓式评估并没有意义,例如:
cout<<m3;// 读取整个矩阵
m3=m1+m2;
m1=m4;// 在此时需要保证m1的改变不会影响到m3,即m1和m3的相依关系导致做了额外的维护工作
template<class NumericalType>
class DataCollection{
public:
NumericalType min()const;
NumericalType max()const;
NumericalType avg()const;
...
}
// min、max、avg函数分别返回数据群的最小、最大、平均值
min、max、avg 被调用时才检查所有数据,然后返回检查结果。min、max、avg 常被调用,便可以分期(逐次)摊还随时记录数据群之最大、最小、平均值的成本。【该方法通过将大规模计算分摊到其他操作中,并将计算结果永久性保存下来,降低了单次用户响应时间】// 情形1:1.23临时转换,得到一个临时int类型对象,然后赋值给a
int a = static_cast<int>(1.23);
// 情形2:实参与形参类型不一致,实参转换类型,生成string临时对象
// 该情形只发生在“按值传参”和“const 引用传参”的情况下
// 而在“引用传参”和“指针传参”的场景下,会严格按照类型进行匹配,不会出现类型转换
int countChar(const std::string& str); // 注意参数是 const 引用,所以后续参数传入会产生类型转换,其中就会产生临时值
char *str = "hello world";
countChar(str); // 函数返回,临时对象被销毁
int countChar(std::string &str); // 注意参数是 non-const 引用,所以后面调用错误
countChar(str); // 错误,char*和std::string&类型不兼容
// 片段1
// 在本片段中,编译器会在函数中创建一个匿名的临时变量
// 这个匿名变量的值,是经过有名变量temp的拷贝得来(生成匿名变量后,程序员设置的有名局部变量temp会被析构)
// 在主函数中,编译器会运用匿名临时变量来拷贝构造结果,在这里就是函数外部的result
// 经过分析,可以知道,没有优化的版本是必然有额外的构造和析构代价
MyClass result = obj1 * obj2;
const MyClass operator * (const MyClass& lhs, const MyClass& rhs)
{
MyClass temp = // do something
return temp;
}
// 片段2【协助RVO优化的版本】【以构造函数替代临时对象】
// 直接返回一个构造函数,即程序员不再显式建立一个临时变量temp
const MyClass operator * (const MyClass& lhs, const MyClass& rhs)
{
return MyClass(lhs.num() * rhs.num(), lhs.num0() * rhs.num0());
}
const MyClass operator * (const MyClass& lhs, const MyClass& rhs)
{
MyClass tmp(lhs.num() * rhs.num(), lhs.num0() * rhs.num0());
return tmp;
}
const UInt operator+(int lhs, const UInt& rhs);
const UInt operator+(const UInt& lhs, int rhs);
const UInt operator+(const UInt& lhs, const UInt& rhs);
// 以上都正确,下面错误
const UInt operator+(int lhs, int rhs); // 重载的和库中的签名一致(即函数名和参数变量均一致),错误。
+= 取代 + 的使用。【本条款其实也是基于临时对象而提出】+ 都是由 += 实现得来,如果 + 的实现版本不是基于 +=,那么可能 result=a+b+c+d 没有 result+=a+=b+=c+=d 效率更快。【因为使用 + 的式子可能会产生三个临时变量】+= 操作符来实现 + 操作符:【用 += 来实现 + 是《Effective C++》条款 21 中介绍的方法,其实就相当于只有 += 需要被维护】template<class T>
T& operator+=(const T& rhs);
// 可以使用template来实现
template<class T>
const T operator +(const T& lhs, const T& rhs)
{
return T(lhs) += rhs;
}
+= 实现 + 能避免临时对象的开销,仍然是因为 RVO:
T(lhs) 调用了拷贝构造函数产生了一个临时对象,而这个临时对象被用来调用 +=,仍然返回该临时对象的引用(也就是返回自身)。【所以编译器就可以利用 RVO 来优化】 template<class T>
const T operator +(const T& lhs, const T& rhs)
{
T result(lhs); // 将 lhs 复制给 result
return result += rhs; // 将lhs 加到 result 上然后返回
}
成本一:维护 virtual table。
成本二:维护 virtual table pointer。
成本三:虚函数与 inline 的互动。【成本就是丧失了 inline 的使用】
成本四:虚函数和多重继承的互动。【多重继承机制导致开销加大,而设置为虚继承,能稍微减轻】

成本四:RTTI(runtime type identification,即运行时类型识别)。【RTTI 与虚函数息息相关】
typeid 操作符取得 class 对应的 type_info 对象。