1.运算符重载只是一种愈发上的方便,也就是说它只是另一种函数调用的方式;
2.只有在能涉及类的代码更易写,尤其是更易读时(请记住,读代码的机会比写代码的机会多多了),才有理由重载运算符。
3.函数的参数表中的参数个数取决于两个因素
a运算符是一元的还是二元的 b运算符被定义为全局还是成员函数
4.
friend const Integer
operator++(Integer& a, int);///友元
const Integer operator++(Integer& a, int) {
cout << "Integer++\n";//后置自增
Integer before(a.i);
a.i++;
return before;
}
5.
const Byte operator++(int) { // 是成员函数而且是内联的
cout << "Byte++\n";
Byte before(b);
b++;
return before;
}
6.后缀版本,编译器为int参数传递一个哑元常量值,用来为后缀版产生不同的标记
7.
Byte& operator=(const Byte& right) {
// Handle self-assignment:
if(this == &right) return *this;//防止自赋值
b = right.b;
return *this;
}
8.一般重载的A+A;也可以A+B
9.参数一般为引用,有些为const有些不是const
10.返回类型也是有const有非const的
11.对于表达式(a++).fun();fun()作用在通过后缀operator++返回的临时对象上,临时对象自动定为常量哦
12.return lei(left.i+right.i);........返回值优化
13.指针间接引用运算符一定是一个成员函数。它有着额外的,非典型的限制:它必须返回一个对象(或引用),该对象也有一个指针间接引用运算符;或者必须返回一个指针,被用于选择指针间接引用运算符箭头所指向的内容。
14。返回指针
Obj* operator->() const {
return oc.a[index];//返回的是指针
}
};
15.类被设置为友元之前,必须知道该类是存在的,与函数不同
class lei;
friend lei;
16.typedef int (Dog::*PMF)(int) const;
PMF是一个typedef,用于简化定义一个指向Dog成员函数的指向成员的指针
17.class IntArray {
enum { sz = 5 };
int i[sz];
public:
IntArray() { memset(i, 0, sz* sizeof(*i)); }
int& operator[](int x) {
require(x >= 0 && x < sz,
"IntArray::operator[] out of range");
return i[x];
}
18
Operator
所有一元运算符 建议成员
= ( ) [ ] –> –>* 必须是成员
+= –= /= *= ^= 建议成员
&= |= %= >>= <<= 建议成员
所有其他的二元运算符 建议非成员
19.
MyType b;
MyType a = b;//被优化成MyType a (b);
a = b;//调用拷贝构造函数
20.类里含有指针时
a.当使用一个赋值运算或一个拷贝构造函数时,最简单的方法是拷贝这个指针所涉及的一切
b.使用计数方法
class A{int num;}
class B{A *pt;}//在使用拷贝构造函数和operator=会使用到
21.因为不止一个对象使用这个Dog。为了解决这个“别名”问题,经常使用另外一个称为“写拷贝”的技术。如果引用计数大于1,在写之前必须拷贝这块存储单元,这样不会影响他人了
void attach() {
++refcount;//这是计数的变量
}
void detach() {
require(refcount != 0);
if(--refcount == 0) delete this;
}
Dog* unalias() {
if(refcount == 1) return this;
--refcount;
return new Dog(*this);
}
class DogHouse {
Dog* p;
string houseName;
public:
DogHouse(Dog* dog, const string& house)
: p(dog), houseName(house) {
}
DogHouse(const DogHouse& dh)
: p(dh.p),
houseName("copy-constructed " + dh.houseName) {
p->attach();//拷贝构造函数的使得refcount加1.
}
DogHouse& operator=(const DogHouse& dh) {
if(&dh != this) {/先判断是否自赋值
houseName = dh.houseName + " assigned";
p->detach();/处理左侧对象
p = dh.p;
p->attach();//右对象的计数加1
}
return *this;
}
22.operator=处理等号左侧是已经创建的对象,所以它首先必须通过为Dog调用detach()允许来整理这个存储单元。如果没有其他对象使用它,这个老的Dog将被销毁,然后operator重复拷贝构造函数的行为。注意它首先检查是否自赋值。
23.如果不想让人执行拷贝构造函数或赋值运算,可以把它们声明为private函数
24.自动类型转换:a.构造函数转换 b.运算符转换。
25.构造函数转换:如果定义一个构造函数,它把另一个类型对象或引用作为它的单个参数,那么这个构造函数允许编译器执行自动类型转换。
class One {
public:
One() {}
};
class Two {
public:
Two(const One&) {}
};
void f(Two) {}
int main() {
One one;
f(one); // Wants a Two, has a One
} ///:~
26.加上explicit阻止自动转换,得手动!加上explicit
class One {
public:
One() {}
};
class Two {
public:
explicit Two(const One&) {}
};
void f(Two) {}
int main() {
One one;
//f(one); //失败的
f(Two(one));
} ///:~
27.运算符转换:创建一个成员函数,这个函数通过在关键字operator后跟随想要转换到的类型的方法,将当前类型转换为希望的类型
class Three {
int i;
public:
Three(int ii = 0, int = 0) : i(ii) {}
};
class Four {
int x;
public:
Four(int xx) : x(xx) {}
operator Three() const { return Three(x); }
};
void g(Three) {}
int main() {
Four four(1);
g(four);///执行 operator Three();
g(1); // 执行 Three(1,0)
} ///:~
28.使用全局重载运算符而不用成员运算符的最便利的原因之一是在全局版本中的自动类型转换可以针对左右任一操作数,而成员版本必须保证左侧操作数已处于正确的形式。如果向两个操作数都被转换,全局版本可以节省很多代码
class Number {
int i;
public:
Number(int ii = 0) : i(ii) {}//必须有这个构造函数,才会转换啊!
const Number/
operator+(const Number& n) const {///成员
return Number(i + n.i);
}
friend const Number//友元
operator-(const Number&, const Number&);
};
const Number
operator-(const Number& n1,
const Number& n2) {
return Number(n1.i - n2.i);
}
int main() {
Number a(47), b(11);
a + b; // OK
a + 1; // 2nd arg converted to Number
//! 1 + a; // Wrong! 1st arg not of type Number
a - b; // OK
a - 1; // 2nd arg converted to Number
1 - a; // 1st arg converted to Number
} ///:
29.‘
例子
class Stringc {
string s;
public:
Stringc(const string& str = "") : s(str) {}
operator const char*() const {
return s.c_str();
}
};
int main() {
Stringc s1("hello"), s2("there");
strcmp(s1, s2); // Standard C function
strspn(s1, s2); // Any string function!
} ///:~因为编译器知道如何从string转换到char*,所以现在任何一个接受char*参数的函数也可以接受string参数
30.自动类型转换应小心使用,同所有重载的运算符相比,它在减少代码方面是非常出色的,但不值得无缘无故地使用。
2.只有在能涉及类的代码更易写,尤其是更易读时(请记住,读代码的机会比写代码的机会多多了),才有理由重载运算符。
3.函数的参数表中的参数个数取决于两个因素
a运算符是一元的还是二元的 b运算符被定义为全局还是成员函数
4.
friend const Integer
operator++(Integer& a, int);///友元
const Integer operator++(Integer& a, int) {
cout << "Integer++\n";//后置自增
Integer before(a.i);
a.i++;
return before;
}
5.
const Byte operator++(int) { // 是成员函数而且是内联的
cout << "Byte++\n";
Byte before(b);
b++;
return before;
}
6.后缀版本,编译器为int参数传递一个哑元常量值,用来为后缀版产生不同的标记
7.
Byte& operator=(const Byte& right) {
// Handle self-assignment:
if(this == &right) return *this;//防止自赋值
b = right.b;
return *this;
}
8.一般重载的A+A;也可以A+B
9.参数一般为引用,有些为const有些不是const
10.返回类型也是有const有非const的
11.对于表达式(a++).fun();fun()作用在通过后缀operator++返回的临时对象上,临时对象自动定为常量哦
12.return lei(left.i+right.i);........返回值优化
13.指针间接引用运算符一定是一个成员函数。它有着额外的,非典型的限制:它必须返回一个对象(或引用),该对象也有一个指针间接引用运算符;或者必须返回一个指针,被用于选择指针间接引用运算符箭头所指向的内容。
14。返回指针
Obj* operator->() const {
return oc.a[index];//返回的是指针
}
};
15.类被设置为友元之前,必须知道该类是存在的,与函数不同
class lei;
friend lei;
16.typedef int (Dog::*PMF)(int) const;
PMF是一个typedef,用于简化定义一个指向Dog成员函数的指向成员的指针
17.class IntArray {
enum { sz = 5 };
int i[sz];
public:
IntArray() { memset(i, 0, sz* sizeof(*i)); }
int& operator[](int x) {
require(x >= 0 && x < sz,
"IntArray::operator[] out of range");
return i[x];
}
18
Operator
所有一元运算符 建议成员
= ( ) [ ] –> –>* 必须是成员
+= –= /= *= ^= 建议成员
&= |= %= >>= <<= 建议成员
所有其他的二元运算符 建议非成员
19.
MyType b;
MyType a = b;//被优化成MyType a (b);
a = b;//调用拷贝构造函数
20.类里含有指针时
a.当使用一个赋值运算或一个拷贝构造函数时,最简单的方法是拷贝这个指针所涉及的一切
b.使用计数方法
class A{int num;}
class B{A *pt;}//在使用拷贝构造函数和operator=会使用到
21.因为不止一个对象使用这个Dog。为了解决这个“别名”问题,经常使用另外一个称为“写拷贝”的技术。如果引用计数大于1,在写之前必须拷贝这块存储单元,这样不会影响他人了
void attach() {
++refcount;//这是计数的变量
}
void detach() {
require(refcount != 0);
if(--refcount == 0) delete this;
}
Dog* unalias() {
if(refcount == 1) return this;
--refcount;
return new Dog(*this);
}
class DogHouse {
Dog* p;
string houseName;
public:
DogHouse(Dog* dog, const string& house)
: p(dog), houseName(house) {
}
DogHouse(const DogHouse& dh)
: p(dh.p),
houseName("copy-constructed " + dh.houseName) {
p->attach();//拷贝构造函数的使得refcount加1.
}
DogHouse& operator=(const DogHouse& dh) {
if(&dh != this) {/先判断是否自赋值
houseName = dh.houseName + " assigned";
p->detach();/处理左侧对象
p = dh.p;
p->attach();//右对象的计数加1
}
return *this;
}
22.operator=处理等号左侧是已经创建的对象,所以它首先必须通过为Dog调用detach()允许来整理这个存储单元。如果没有其他对象使用它,这个老的Dog将被销毁,然后operator重复拷贝构造函数的行为。注意它首先检查是否自赋值。
23.如果不想让人执行拷贝构造函数或赋值运算,可以把它们声明为private函数
24.自动类型转换:a.构造函数转换 b.运算符转换。
25.构造函数转换:如果定义一个构造函数,它把另一个类型对象或引用作为它的单个参数,那么这个构造函数允许编译器执行自动类型转换。
class One {
public:
One() {}
};
class Two {
public:
Two(const One&) {}
};
void f(Two) {}
int main() {
One one;
f(one); // Wants a Two, has a One
} ///:~
26.加上explicit阻止自动转换,得手动!加上explicit
class One {
public:
One() {}
};
class Two {
public:
explicit Two(const One&) {}
};
void f(Two) {}
int main() {
One one;
//f(one); //失败的
f(Two(one));
} ///:~
27.运算符转换:创建一个成员函数,这个函数通过在关键字operator后跟随想要转换到的类型的方法,将当前类型转换为希望的类型
class Three {
int i;
public:
Three(int ii = 0, int = 0) : i(ii) {}
};
class Four {
int x;
public:
Four(int xx) : x(xx) {}
operator Three() const { return Three(x); }
};
void g(Three) {}
int main() {
Four four(1);
g(four);///执行 operator Three();
g(1); // 执行 Three(1,0)
} ///:~
28.使用全局重载运算符而不用成员运算符的最便利的原因之一是在全局版本中的自动类型转换可以针对左右任一操作数,而成员版本必须保证左侧操作数已处于正确的形式。如果向两个操作数都被转换,全局版本可以节省很多代码
class Number {
int i;
public:
Number(int ii = 0) : i(ii) {}//必须有这个构造函数,才会转换啊!
const Number/
operator+(const Number& n) const {///成员
return Number(i + n.i);
}
friend const Number//友元
operator-(const Number&, const Number&);
};
const Number
operator-(const Number& n1,
const Number& n2) {
return Number(n1.i - n2.i);
}
int main() {
Number a(47), b(11);
a + b; // OK
a + 1; // 2nd arg converted to Number
//! 1 + a; // Wrong! 1st arg not of type Number
a - b; // OK
a - 1; // 2nd arg converted to Number
1 - a; // 1st arg converted to Number
} ///:
29.‘
例子
class Stringc {
string s;
public:
Stringc(const string& str = "") : s(str) {}
operator const char*() const {
return s.c_str();
}
};
int main() {
Stringc s1("hello"), s2("there");
strcmp(s1, s2); // Standard C function
strspn(s1, s2); // Any string function!
} ///:~因为编译器知道如何从string转换到char*,所以现在任何一个接受char*参数的函数也可以接受string参数
30.自动类型转换应小心使用,同所有重载的运算符相比,它在减少代码方面是非常出色的,但不值得无缘无故地使用。