补充重载运算符
重载运算符
http://blog.163.com/zhoumhan_0351/blog/static/399542272010027113311390
1、只有那些包含用户自定义类型的表达式才能有重载的运算符。
return int(2+3);
返回临时对象。临时对象必然是常对象,且不会执行析构函数,只需要一个普通的构造函数调用。效率高,称为返回值优化。
operator->:指针间接引用运算符一定是一个成员函数,必须返回对象或引用,或指针。
operator->*:必须返回对象。对于这个对象,可以用正在调用的成员函数为参数调用operator()。operator()调用必须是成员函数,它是惟一允许在它里面有任意个参数的函数。
//: C12:PointerToMemberOperator.cpp
#include <iostream>
using namespace std;
class Dog {
public:
int run(int i) const {
cout << "run\n";
return i;
}
int eat(int i) const {
cout << "eat\n";
return i;
}
int sleep(int i) const {
cout << "ZZZ\n";
return i;
}
typedef int (Dog::*PMF)(int) const;
// operator->* must return an object
// that has an operator():
class FunctionObject {
Dog* ptr;
PMF pmem;
public:
// Save the object pointer and member pointer
FunctionObject(Dog* wp, PMF pmf)
: ptr(wp), pmem(pmf) {
cout << "FunctionObject constructor\n";
}
// Make the call using the object pointer
// and member pointer
int operator()(int i) const {
cout << "FunctionObject::operator()\n";
return (ptr->*pmem)(i); // Make the call
}
};
FunctionObject operator->*(PMF pmf) {
cout << "operator->*" << endl;
return FunctionObject(this, pmf);
}
};
int main() {
Dog w;
Dog::PMF pmf = &Dog::run;
cout << (w->*pmf)(1) << endl;
pmf = &Dog::sleep;
cout << (w->*pmf)(2) << endl;
pmf = &Dog::eat;
cout << (w->*pmf)(3) << endl;
} ///:~
2、基本方针
3、构造函数的调用
#include "iostream"
using namespace std;
class Fi{
public:
Fi() {}
};
class Fee{
private:
int i;
public:
Fee(int i) {cout<<"Fee(int)"<<endl;}
Fee(const Fi&) {cout<<"Fee(const Fi Fi)"<<endl;}
};
int main()
{
Fee fee=1;
Fee fee2(1);//效果一样
Fi fi;
Fee fum=fi;
Fee fum2(fi);
return 1;
}
应当注意:给两个相同类型的对象赋值时,应当检查一下自赋值。
DogHouse& operator=(const DogHouse& dh) {
// Check for self-assignment:
if(&dh != this) {
p = new Dog(dh.p, " assigned");
houseName = dh.houseName + " assigned";
}
return *this;
}
4、引用计数
如果对象需要大量的内存来运行拷贝构造函数。应当避免。一种方法是使用引用引数。使一块存储单元具有智能,它知道有多少对象指向它。拷贝构造函数或赋值运算意味着把另外的指针指向现在的存储单元并增加引用计数,反之。
此外,To solve this“aliasing”problem(多个指针指向同一块内存时的修改问题),引入写拷贝技术:在向这块存储单元写入前,应该确信没有他人在用,如果引用计数大于1,写之前应拷贝这块存储单元。
Dog* unalias() {
cout << "Unaliasing Dog: " << *this << endl;
// Don't duplicate if not aliased:
if(refcount == 1) return this;
--refcount;
// Use copy-constructor to duplicate:
return new Dog(*this);
}
5、如果用户没有创建type::operator=(type),则编译器会自动创建一个。
#include <iostream>
using namespace std;
class Cargo {
public:
Cargo& operator=(const Cargo&) {
cout << "inside Cargo::operator=()" << endl;
return *this;
}
};
class Truck {
Cargo b;
};
int main() {
Truck a, b;
a = b; // Prints: "inside Cargo::operator=()"
} ///:~
为Truck自动生成的operator调用Cargo& operator=。
6、自动类型转换
关于类型转换函数和转换构造函数的讨论
http://blog.163.com/zhoumhan_0351/blog/static/39954227201002702424657
1)构造函数转换
//: C12:AutomaticOperatorEquals.cpp
class One {
public:
One() {}
};
class Two {
public:
Two(const One&) {}
explicit Two(const One&) {}
};
void f(Two) {}
int main() {
One one;
f(one); // Wants a Two, has a One
} ///:~
有时这可能会问题,可在构造函数加explicit来阻止显式的自动转换。创建一个单一参数的构造函数总是定义一个自动类型转换(即使不止一个参数,其它参数将被默认处理)。
2)运算符转换
7、类型转换的例子
如果不用自动类型转换就想用从标准的C库函数中使用所有的字符串函数,就得为每个函数写一个相应的成员函数,如下所示:
int strcmp(const Stringc& S) const {
return ::strcmp(s.c_str(), S.s.c_str());
}
.........
如果用自动类型转换:
operator const char*() const {
return s.c_str();
}
则编译器会知道如何将当前的string型转换成char*,所以任何一个接受char*的函数也可接收string参数。
当然,有时自动类型转换可能会产生二义性,解决方法是只定义一种。
class Orange {};
class Pear {};
class Apple {
public:
operator Orange() const;
operator Pear() const;
};
// Overloaded eat():
void eat(Orange);
void eat(Pear);
int main() {
Apple c;
//! eat(c);
// Error: Apple -> Orange or Apple -> Pear ???
} ///:~