目录
支持+/-操作符的复数类
支持+=/-=操作符的复数类
支持-/~操作符的复数类
支持前++/–操作符的复数类
支持后++/–操作符的复数类
支持<<操作符的复数类
支持>>操作符的复数类
支持[]操作符的数组类
支持()操作符的平方类
简化版的智能指针类
auto_ptr的基本用法和局限
在基本类型和类类型之间做类型转换
重载new和delete操作符
1 支持+/-操作符的复数类
1.1 问题
定义一个复数类,类中包括两个私有的成员变量,m_r代表实部,m_i代表虚部,并重载+/-运算符,以实现复数的加法与减法运算,及显示运算结果。
1.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:支持+/-操作符的复数类
代码如下:
#include
class Complex
{
private:
double m_r;//实部
double m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex const operator+ (Complex const& rhs) const
{
return Complex (m_r + rhs.m_r, m_i + rhs.m_i);
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex const operator- (Complex const&, Complex const&);
};
Complex const operator- (Complex const& lhs, Complex const& rhs)
{
return Complex (lhs.m_r - rhs.m_r, lhs.m_i - rhs.m_i);
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
Complex c2(4, 1);
Complex c3;
c3 = c1 + c2;
c3.show();
c3 = c1 - c2;
c3.show();
return 0;
}
上述代码中,以下代码:
Complex const operator+ (Complex const& rhs) const
{
return Complex (m_r + rhs.m_r, m_i + rhs.m_i);
}
定义了复数类Complex的+运算符的重载函数。常函数以支持右值型左操作数,常参数以支持右值型右操作数,常返回值以支持右值型表达式的值。
上述代码中,以下代码:
Complex const operator- (Complex const& lhs, Complex const& rhs)
{
return Complex (lhs.m_r - rhs.m_r, lhs.m_i - rhs.m_i);
}
定义了复数类Complex的-运算符的全局函数。常第一参数以支持右值型左操作数,常第二参数以支持右值型右操作数,常返回值以支持右值型表达式的值。
上述代码中,以下代码:
friend Complex const operator- (Complex const&, Complex const&);
为了在一个全局操作符函数中直接访问其操作数类型的私有及保护成员,同时又不破坏其操作数类型的封装性,可以将该操作符函数声明为其操作数类型的友元。
1.3 完整代码
本案例的完整代码如下所示:
#include
class Complex
{
private:
double m_r;//实部
double m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex const operator+ (Complex const& rhs) const
{
return Complex (m_r + rhs.m_r, m_i + rhs.m_i);
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex const operator- (Complex const&, Complex const&);
};
Complex const operator- (Complex const& lhs, Complex const& rhs)
{
return Complex (lhs.m_r - rhs.m_r, lhs.m_i - rhs.m_i);
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
Complex c2(4, 1);
Complex c3;
c3 = c1 + c2;
c3.show();
c3 = c1 - c2;
c3.show();
return 0;
}
2 支持+=/-=操作符的复数类
2.1 问题
定义一个复数类,类中包括两个私有的成员变量,m_r代表实部,m_i代表虚部,并重载+=/-=运算符,以实现复数的复合赋值运算,及显示运算结果。
2.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:支持+=/-=操作符的复数类
代码如下:
#include
class Complex
{
private:
double m_r;//实部
double m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex& operator+= (Complex const& rhs)
{
m_r += rhs.m_r;
m_i += rhs.m_i;
return *this;
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex& operator-= (Complex&, Complex const&);
};
Complex& operator-= (Complex& lhs, Complex const& rhs)
{
lhs.m_r -= rhs.m_r;
lhs.m_i -= rhs.m_i;
return lhs;
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
Complex c2(4, 1);
c1 += c2;
c1.show();
c1 -= c2;
c1.show();
return 0;
}
上述代码中,以下代码:
Complex& operator+= (Complex const& rhs)
{
m_r += rhs.m_r;
m_i += rhs.m_i;
return *this;
}
定义了复数类Complex的+=运算符的重载函数。非常函数以支持左值型左操作数,常参数以支持右值型右操作数,非常返回值以支持左值型表达式的值,返回自引用即左操作数本身。
上述代码中,以下代码:
Complex& operator-= (Complex& lhs, Complex const& rhs)
{
lhs.m_r -= rhs.m_r;
lhs.m_i -= rhs.m_i;
return lhs;
}
定义了复数类Complex的-=运算符的全局函数。非常第一参数以支持左值型左操作数,常第二参数以支持右值型右操作数,非常返回值以支持左值型表达式的值,返回引用型第一参数即左操作数本身。
上述代码中,以下代码:
friend Complex const operator- (Complex const&, Complex const&);
为了在一个全局操作符函数中直接访问其操作数类型的私有及保护成员,同时又不破坏其操作数类型的封装性,可以将该操作符函数声明为其操作数类型的友元。
2.3 完整代码
本案例的完整代码如下所示:
#include
class Complex
{
private:
double m_r;//实部
double m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex& operator+= (Complex const& rhs)
{
m_r += rhs.m_r;
m_i += rhs.m_i;
return *this;
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex& operator-= (Complex&, Complex const&);
};
Complex& operator-= (Complex& lhs, Complex const& rhs)
{
lhs.m_r -= rhs.m_r;
lhs.m_i -= rhs.m_i;
return lhs;
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
Complex c2(4, 1);
c1 += c2;
c1.show();
c1 -= c2;
c1.show();
return 0;
}
3 支持-/~操作符的复数类
3.1 问题
定义一个复数类,类中包括两个私有的成员变量,m_r代表实部,m_i代表虚部,并重载-/~运算符,以实现复数的相反数和位反运算,及显示运算结果。
3.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:支持-/~操作符的复数类
代码如下所示:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex const operator- (void) const
{
return Complex (-m_r, -m_i);
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex const operator~ (Complex const&);
};
Complex const operator~ (Complex const& lhs)
{
return Complex (~lhs.m_r, ~lhs.m_i);
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
Complex c2;
c2 = -c1;
c2.show();
c2 = ~c1;
c2.show();
return 0;
}
上述代码中,以下代码:
Complex const operator- (void) const
{
return Complex (-m_r, -m_i);
}
定义了复数类Complex的-运算符的重载函数。常函数以支持右值型操作数,常返回值以支持右值型表达式的值。
上述代码中,以下代码:
Complex const operator~ (Complex const& lhs)
{
return Complex (~lhs.m_r, ~lhs.m_i);
}
定义了复数类Complex的~运算符的全局函数。
上述代码中,以下代码:
friend Complex const operator~ (Complex const&);
为了在一个全局操作符函数中直接访问其操作数类型的私有及保护成员,同时又不破坏其操作数类型的封装性,可以将该操作符函数声明为其操作数类型的友元。
3.3 完整代码
本案例的完整代码如下所示:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex const operator- (void) const
{
return Complex (-m_r, -m_i);
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex const operator~ (Complex const&);
};
Complex const operator~ (Complex const& lhs)
{
return Complex (~lhs.m_r, ~lhs.m_i);
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
Complex c2;
c2 = -c1;
c2.show();
c2 = ~c1;
c2.show();
return 0;
}
4 支持前++/–操作符的复数类
4.1 问题
定义一个复数类,类中包括两个私有的成员变量,m_r代表实部,m_i代表虚部,并重载前++/–运算符,以实现复数的前置自增/前置自减运算,及显示运算结果。
4.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:支持前++/–操作符的复数类
代码如下所示:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex& operator++ (void)
{
++m_r;
++m_i;
return *this;
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex& operator-- (Complex&);
};
Complex& operator-- (Complex& opd)
{
–opd.m_r;
–opd.m_i;
return opd;
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
++c1;
c1.show();
--c1;
c1.show();
return 0;
}
上述代码中,以下代码:
Complex& operator++ (void)
{
++m_r;
++m_i;
return *this;
}
定义了复数类Complex的前置++运算符的重载函数。非常参数以支持左值型操作数,非常返回值以支持左值型表达式的值,返回自引用即操作数本身。
上述代码中,以下代码:
Complex& operator-- (Complex& opd)
{
–opd.m_r;
–opd.m_i;
return opd;
}
定义了复数类Complex的前置–运算符的全局函数。非常函数以支持左值型操作数,非常返回值以支持左值型表达式的值,返回自引用即操作数本身。
上述代码中,以下代码:
friend Complex& operator-- (Complex&);
为了在一个全局操作符函数中直接访问其操作数类型的私有及保护成员,同时又不破坏其操作数类型的封装性,可以将该操作符函数声明为其操作数类型的友元。
4.3 完整代码
本案例的完整代码如下所示:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex& operator++ (void)
{
++m_r;
++m_i;
return *this;
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex& operator-- (Complex&);
};
Complex& operator-- (Complex& opd)
{
–opd.m_r;
–opd.m_i;
return opd;
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
++c1;
c1.show();
--c1;
c1.show();
return 0;
}
5 支持后++/–操作符的复数类
5.1 问题
定义一个复数类,类中包括两个私有的成员变量,m_r代表实部,m_i代表虚部,并重载后++/–运算符,以实现复数的后置自增/后置自减运算,及显示运算结果。
5.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:支持后++/–操作符的复数类
代码如下所示:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex const operator++ (int)
{
Complex old = *this;
++m_r;
++m_i;
return old;
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex const operator-- (Complex&, int);
};
Complex const operator-- (Complex& opd, int)
{
Complex old = opd;
–opd.m_r;
–opd.m_i;
return old;
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
Complex c2;
c2 = c1++;
c1.show();
c2.show();
c2 = c1--;
c1.show();
c2.show();
return 0;
}
上述代码中,以下代码:
Complex const operator++ (int)
{
Complex old = *this;
++m_r;
++m_i;
return old;
}
定义了复数类Complex的后置++运算符的重载函数。非常函数以支持左值型操作数,常返回值以支持右值型表达式的值,整型哑元参数以区别于前置自增。由于是后置自增运算,所以操作数应该先参与其他运算符的运算,然后再自增,这样就必须返回自增前的对象,即old,使自增前的对象与其它运算符运算。对于后置自增表达式,编译器在调用操作符函数时会多传一个整型参数,通过重载解析匹配到后缀操作符函数。
上述代码中,以下代码:
Complex const operator-- (Complex& opd, int)
{
Complex old = opd;
–opd.m_r;
–opd.m_i;
return old;
}
定义了复数类Complex的后置–运算符的全局函数。非常参数以支持左值型操作数,常返回值以支持右值型表达式的值,整型哑元参数以区别于前置自减。
上述代码中,以下代码:
friend Complex const operator-- (Complex&, int);
为了在一个全局操作符函数中直接访问其操作数类型的私有及保护成员,同时又不破坏其操作数类型的封装性,可以将该操作符函数声明为其操作数类型的友元。
5.3 完整代码
本案例的完整代码如下所示:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
Complex const operator++ (int)
{
Complex old = *this;
++m_r;
++m_i;
return old;
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend Complex const operator-- (Complex&, int);
};
Complex const operator-- (Complex& opd, int)
{
Complex old = opd;
–opd.m_r;
–opd.m_i;
return old;
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
Complex c2;
c2 = c1++;
c1.show();
c2.show();
c2 = c1--;
c1.show();
c2.show();
return 0;
}
6 支持<<操作符的复数类
6.1 问题
定义一个复数类,类中包括两个私有的成员变量,m_r代表实部,m_i代表虚部,并重载<<输出流运算符,以实现复数的输出操作。
6.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:支持<<操作符的复数类
代码如下:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend std::ostream& operator<< (std::ostream&, Complex const&);
};
std::ostream& operator<< (std::ostream& lhs, Complex const& rhs)
{
return lhs << rhs.m_r << ‘+’ << rhs.m_i << ‘i’;
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
std::cout << c1 << std::endl;
return 0;
}
上述代码中,以下代码:
std::ostream& operator<< (std::ostream& lhs, Complex const& rhs)
{
return lhs << rhs.m_r << ‘+’ << rhs.m_i << ‘i’;
}
定义了复数类Complex的<<输出流运算符的全局函数。因为左操作数的类型为ostream,若以成员函数形式重载该操作符,就应将其定义为ostream类的成员,该类为标准库提供,无法添加新的成员,因此只能以全局函数形式重载该操作符。非常第一参数以支持左值型左操作数,常第二参数以支持右值型右操作数,非常返回值以支持左值型表达式的值,返回引用型第一参数即左操作数本身。
上述代码中,以下代码:
friend std::ostream& operator<< (std::ostream&, Complex const&);
为了在一个全局操作符函数中直接访问其操作数类型的私有及保护成员,同时又不破坏其操作数类型的封装性,可以将该操作符函数声明为其操作数类型的友元。
6.3 完整代码
本案例的完整代码如下所示:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend std::ostream& operator<< (std::ostream&, Complex const&);
};
std::ostream& operator<< (std::ostream& lhs, Complex const& rhs)
{
return lhs << rhs.m_r << ‘+’ << rhs.m_i << ‘i’;
}
int main(int argc, const char * argv[])
{
Complex c1(3, 5);
std::cout << c1 << std::endl;
return 0;
}
7 支持>>操作符的复数类
7.1 问题
定义一个复数类,类中包括两个私有的成员变量,m_r代表实部,m_i代表虚部,并重载>>输入流运算符,以实现复数的输入操作。
7.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:支持>>操作符的复数类
使用递归方法,将数组先分成前后两部分,然后再将前一部分分解,最后将后一部分分解。
代码如下:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend std::istream& operator>> (std::istream&, Complex&);
friend std::ostream& operator<< (std::ostream&, Complex const&);
};
std::istream& operator>> (std::istream& lhs, Complex& rhs)
{
return lhs >> rhs.m_r >> rhs.m_i;
}
std::ostream& operator<< (std::ostream& lhs, Complex const& rhs)
{
return lhs << rhs.m_r << ‘+’ << rhs.m_i << ‘i’;
}
int main(int argc, const char * argv[])
{
Complex c1;
std::cin >> c1;
std::cout << c1 << std::endl;
return 0;
}
上述代码中,以下代码:
std::istream& operator>> (std::istream& lhs, Complex& rhs)
{
return lhs >> rhs.m_r >> rhs.m_i;
}
定义了复数类Complex的>>输入流运算符的全局函数。因为左操作数的类型为istream,若以成员函数形式重载该操作符,就应将其定义为istream类的成员,该类为标准库提供,无法添加新的成员,因此只能以全局函数形式重载该操作符。非常第一参数以支持左值型左操作数,非常第二参数以支持左值型右操作数,非常返回值以支持左值型表达式的值,返回引用型第一参数即左操作数本身。
上述代码中,以下代码:
friend std::istream& operator>> (std::istream&, Complex&);
为了在一个全局操作符函数中直接访问其操作数类型的私有及保护成员,同时又不破坏其操作数类型的封装性,可以将该操作符函数声明为其操作数类型的友元。
7.3 完整代码
本案例的完整代码如下所示:
#include
class Complex
{
private:
int m_r;//实部
int m_i;//虚部
public:
Complex (double r = 0, double i = 0) : m_r®, m_i(i)
{
}
void show (void) const
{
std::cout << m_r << “+” << m_i << “i” << std::endl;
}
friend std::istream& operator>> (std::istream&, Complex&);
friend std::ostream& operator<< (std::ostream&, Complex const&);
};
std::istream& operator>> (std::istream& lhs, Complex& rhs)
{
return lhs >> rhs.m_r >> rhs.m_i;
}
std::ostream& operator<< (std::ostream& lhs, Complex const& rhs)
{
return lhs << rhs.m_r << ‘+’ << rhs.m_i << ‘i’;
}
int main(int argc, const char * argv[])
{
Complex c1;
std::cin >> c1;
std::cout << c1 << std::endl;
return 0;
}
8 支持[]操作符的数组类
8.1 问题
下标[]操作符重载常用于在容器类型中以下标方式获取数据元素,对于非常容器的元素为左值,对于常容器的元素为右值。
8.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:支持[]操作符的数组类
代码如下:
#include
class Array
{
private:
int m_array[256];
public:
Array (void)
{
memset(m_array, 0, sizeof(m_array));
}
int& operator[] (size_t i)
{
return m_array[i];
}
const int& operator[] (size_t i) const
{
return const_cast<Array&> (*this)[i];
}
};
int main(int argc, const char * argv[])
{
Array array;
array[100] = 1000;
std::cout << array.operator[] (100) << std::endl;
const Array& carray = array;
std::cout << carray[100] << std::endl;
return 0;
}
上述代码中,以下代码:
int& operator[] (size_t i)
{
return m_array[i];
}
定义了容器类Array的下标[]操作符重载。由于非常容器的元素可能为左值,如在主程序中的以下代码:
array[100] = 1000;
所以,下标[]操作符的重载函数的返回值必须是引用类型。
上述代码中,以下代码:
const int& operator[] (size_t i) const
{
return const_cast<Array&> (*this)[i];
}
是由于常容器的元素只能作为右值,如在主程序中的以下代码:
const Array& carray = array;
std::cout << carray[100] << std::endl;
carray即为常容器对象,常容器的元素carray[100]因为是常量,所以只能做右值使用。
8.3 完整代码
本案例的完整代码如下所示:
#include
class Array
{
private:
int m_array[256];
public:
Array (void)
{
memset(m_array, 0, sizeof(m_array));
}
int& operator[] (size_t i)
{
return m_array[i];
}
const int& operator[] (size_t i) const
{
return const_cast<Array&> (*this)[i];
}
};
int main(int argc, const char * argv[])
{
Array array;
array[100] = 1000;
std::cout << array.operator[] (100) << std::endl;
const Array& carray = array;
std::cout << std::hex << &array << std::dec << std::endl;
std::cout << carray[100] << std::endl;
return 0;
}
9 支持()操作符的平方类
9.1 问题
如果一个类重载了函数操作符(),那么该类的对象就可以被当做函数来调用,其参数和返回值就是函数操作符函数的参数和返回值,参数的个数、类型以及返回值的类型,没有限制,函数操作符()是唯一可以带有缺省参数的操作符重载函数。
9.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:支持()操作符的平方类
代码如下:
#include
class Square
{
public:
int operator() (int x)
{
return x * x;
}
};
int main(int argc, const char * argv[])
{
Square square;
std::cout << square(5) << std::endl;
return 0;
}
上述代码中,以下代码:
int operator() (int x)
{
return x * x;
}
定义了类Square的函数操作符()重载。当一个类重载了函数操作符()后,该类的对象就可以被当做函数来调用,如在主程序中的以下语句:
std::cout << square(5) << std::endl;
其中square是一个对象,不是函数名,但square(5)的使用方法与调用一个函数没有区别,就是因为重载了函数操作符的缘故。
9.3 完整代码
本案例的完整代码如下所示:
#include
class Square
{
public:
int operator() (int x)
{
return x * x;
}
};
int main(int argc, const char * argv[])
{
Square square;
std::cout << square(5) << std::endl;
return 0;
}
10 简化版的智能指针类
10.1 问题
如果一个类重载了解引用*操作符和间接成员访问->操作符,那么该类的对象就可以被当做指针来使用。智能指针是一个封装了常规指针的类类型对象,当它离开作用域时,其析构函数负责释放该常规指针所指向的动态内存。任何时候,针对同一个对象,只允许有一个智能指针持有其地址,否则该对象将在多个智能指针中被析构多次,因此智能指针的拷贝构造和拷贝赋值需要做特殊处理,对其所持有的对象地址,以指针间的转移代替复制。
10.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:简化版的智能指针类
代码如下:
#include
class Integer
{
private:
int m_val;
public:
Integer (const int& val) : m_val(val)
{
}
int& value (void)
{
return m_val;
}
const int& value (void) const
{
return m_val;
}
};
class IntegerPointer
{
private:
Integer* m_p;
public:
IntegerPointer (Integer* p = NULL) : m_p§
{
}
IntegerPointer (IntegerPointer& p)
{
m_p = p.m_p;
p.m_p = NULL;
}
~IntegerPointer ()
{
delete m_p;
}
IntegerPointer& operator= (IntegerPointer& p)
{
if (&p != this)
{
delete m_p;
m_p = p.m_p;
p.m_p = NULL;
}
return this;
}
Integer& operator (void) const
{
return m_p;
}
Integer operator-> (void) const
{
return m_p;
}
};
int main(int argc, const char * argv[])
{
IntegerPointer ip(new Integer(100));
std::cout << (*ip).value() << std::endl;
ip->value()++;
std::cout << ip->value() << std::endl;
IntegerPointer ip1 = ip;
//std::cout << ip->value() << std::endl;
std::cout << ip1->value() << std::endl;
IntegerPointer ip2(new Integer(200));
ip2 = ip1;
//std::cout << ip1->value() << std::endl;
std::cout << ip2->value() << std::endl;
return 0;
}
上述代码中,以下代码:
class IntegerPointer
定义了一个简化版的智能指针类IntegerPointer。
上述代码中,以下代码:
Integer& operator* (void) const
{
return *m_p;
}
在类IntegerPoiner中重载了解引用操作符,由于解引用操作符可以将指针指向的内存单元的内容取出来,所以该函数返回一个当前指针m_p指向的对象。如在主程序中的以下语句:
std::cout << (*ip).value() << std::endl;
等效于如下语句:
std::cout << (ip.operator*()).value() << std::endl;
其中(ip),即(ip.operator()),得到的是智能指针ip指向的对象。
上述代码中,以下代码:
Integer* operator-> (void) const
{
return m_p;
}
在类IntegerPoiner中重载了间接成员访问->操作符,使得主程序中的如下语句:
std::cout << ip->value() << std::endl;
使得智能指针ip可以访问其指向的对象的成员。
上述代码中,以下代码:
IntegerPointer (IntegerPointer& p)
{
m_p = p.m_p;
p.m_p = NULL;
}
重新定义了拷贝构造函数,这是因为在任何时候,针对同一个对象,只允许有一个智能指针持有其地址,否则该对象将在多个智能指针中被析构多次。如在主程序中的以下语句:
IntegerPointer ip1 = ip;
如果不按上述方法重新定义拷贝构造函数,则智能指针ip和ip1指向同一对象,当两个对象的作用域结束时,都要调用如下析构函数:
~IntegerPointer ()
{
delete m_p;
}
释放其指向的存储空间,当ip和ip1指向的是同一个对象,释放两次,第二次必然会导致程序崩溃。
上述代码中,以下代码:
IntegerPointer& operator= (IntegerPointer& p)
{
if (&p != this)
{
delete m_p;
m_p = p.m_p;
p.m_p = NULL;
}
return *this;
}
重载了赋值=操作符,也是为了防止多个智能指针指向同一个对象导致的被析构多次。如在主程序中的以下语句:
IntegerPointer ip2(new Integer(200));
ip2 = ip1;
当一个智能指针ip1赋值给另一个智能指针ip2时,ip1将变成空指针,以防止多个智能指针指向同一个对象。
10.3 完整代码
本案例的完整代码如下所示:
#include
class Integer
{
private:
int m_val;
public:
Integer (const int& val) : m_val(val)
{
}
int& value (void)
{
return m_val;
}
const int& value (void) const
{
return m_val;
}
};
class IntegerPointer
{
private:
Integer* m_p;
public:
IntegerPointer (Integer* p = NULL) : m_p§
{
}
IntegerPointer (IntegerPointer& p)
{
m_p = p.m_p;
p.m_p = NULL;
}
~IntegerPointer ()
{
delete m_p;
}
IntegerPointer& operator= (IntegerPointer& p)
{
if (&p != this)
{
delete m_p;
m_p = p.m_p;
p.m_p = NULL;
}
return this;
}
Integer& operator (void) const
{
return m_p;
}
Integer operator-> (void) const
{
return m_p;
}
};
int main(int argc, const char * argv[])
{
IntegerPointer ip(new Integer(100));
std::cout << (*ip).value() << std::endl;
ip->value()++;
std::cout << ip->value() << std::endl;
IntegerPointer ip1 = ip;
//std::cout << ip->value() << std::endl;
std::cout << ip1->value() << std::endl;
IntegerPointer ip2(new Integer(200));
ip2 = ip1;
//std::cout << ip1->value() << std::endl;
std::cout << ip2->value() << std::endl;
return 0;
}
11 auto_ptr的基本用法和局限
11.1 问题
auto_ptr模板定义了类似指针的对象,可以将new直接或间接获得的地址赋值给这种对象。当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥有的动态内存自动释放。即使发生异常,通过异常的栈展开过程也能将动态内存释放。
11.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:auto_ptr的基本用法和局限
代码如下:
#include
#include
class Integer
{
private:
int m_val;
public:
Integer (const int& val) : m_val(val)
{
}
int& value (void)
{
return m_val;
}
const int& value (void) const
{
return m_val;
}
};
int main(int argc, const char * argv[])
{
std::auto_ptr<Integer> ip (new Integer(100));
std::cout << (*ip).value() << std::endl;
ip->value()++;
std::cout << ip->value() << std::endl;
std::auto_ptr<Integer> ip1 = ip;
//std::cout << ip->value() << std::endl;
std::cout << ip1->value() << std::endl;
std::auto_ptr<Integer> ip2(new Integer(200));
ip2 = ip1;
//std::cout << ip1->value() << std::endl;
std::cout << ip2->value() << std::endl;
//std::auto_ptr<Integer> ip3(new Integer[10]);
return 0;
}
上述代码中,以下代码:
std::auto_ptr<Integer> ip (new Integer(100));
定义了一个auto_ptr智能指针模板类的对象ip,指向一个由new生成的类Integer的对象,其中表示智能指针的数据类型。
上述代码中,以下代码:
std::cout << (*ip).value() << std::endl;
由于在模板类auto_ptr中重载了解引用*操作符,所以(*ip)即是上面用new生成的类Integer的对象。
上述代码中,以下代码:
std::cout << ip->value() << std::endl;
由于在模板类auto_ptr中重载了间接成员访问->操作符,所以模板类auto_ptr的对象ip可以访问到上面用new生成的类Integer的对象的成员函数。
上述代码中,以下代码:
std::auto_ptr<Integer> ip1 = ip;
//std::cout << ip->value() << std::endl;
std::cout << ip1->value() << std::endl;
由于在模板类auto_ptr中重新定义了拷贝构造函数,所以当将ip初始化给ip1时,ip将自动变成空指针,这是因为在任何时候,针对同一个对象,只允许有一个模板类auto_ptr指针持有其地址,否则该对象将在多个模板类auto_ptr指针中被析构多次,造成程序崩溃。
上述代码中,以下代码:
std::auto_ptr<Integer> ip2(new Integer(200));
ip2 = ip1;
//std::cout << ip1->value() << std::endl;
std::cout << ip2->value() << std::endl;
由于重载了模板类auto_ptr中赋值=操作符,所以当将ip1赋值给ip2时,ip1将自动变成空指针,这也是为了防止同一个对象在多个模板类auto_ptr指针中被析构多次。
上述代码中,以下代码:
//std::auto_ptr<Integer> ip3(new Integer[10]);
不允许让模板类auto_ptr指针指向对象数组。这是因为在模板类auto_ptr中的析构函数是使用delete释放存储空间的,而不是使用delete[]释放。
11.3 完整代码
本案例的完整代码如下所示:
#include
#include
class Integer
{
private:
int m_val;
public:
Integer (const int& val) : m_val(val)
{
}
int& value (void)
{
return m_val;
}
const int& value (void) const
{
return m_val;
}
};
int main(int argc, const char * argv[])
{
std::auto_ptr<Integer> ip (new Integer(100));
std::cout << (*ip).value() << std::endl;
ip->value()++;
std::cout << ip->value() << std::endl;
std::auto_ptr<Integer> ip1 = ip;
//std::cout << ip->value() << std::endl;
std::cout << ip1->value() << std::endl;
std::auto_ptr<Integer> ip2(new Integer(200));
ip2 = ip1;
//std::cout << ip1->value() << std::endl;
std::cout << ip2->value() << std::endl;
//std::auto_ptr<Integer> ip3(new Integer[10]);
return 0;
}
12 在基本类型和类类型之间做类型转换
12.1 问题
由基本数据类型向类数据类型转换,只能通过类类型的构造函数实现。由类数据类型向基本数据类型转换,只能通过类类型的类型转换操作符函数实现。
12.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:在基本类型和类类型之间做类型转换
代码如下:
#include
class Integer
{
private:
int m_val;
public:
Integer (const int& val) : m_val(val)
{
}
int& value (void)
{
return m_val;
}
const int& value (void) const
{
return m_val;
}
operator int (void) const
{
return m_val;
}
};
int main(int argc, const char * argv[])
{
Integer i = 10;
std::cout << i.value() << std::endl;
i = 20;
std::cout << i.value() << std::endl;
int a = i;
std::cout << a << std::endl;
return 0;
}
上述代码中,以下代码:
Integer (const int& val) : m_val(val)
{
}
是类Integer的构造函数,当此构造函数如上只有一个形参时,它还有一个功能就是由基本数据类型int向类数据类型Integer进行转换。如在主程序中,以下语句:
Integer i = 10;
和以下语句:
i = 20;
都是将int型数据赋值给类Integer类型的对象,我们知道,当赋值号两边的数据类型不一致时,会按照左值的类型进行转换。左值的类型为类Integer,所以转换方法就是类Integer的构造函数。
上述代码中,以下代码:
operator int (void) const
{
return m_val;
}
是类Integer的类型转换操作符函数,该函数可以将类数据类型Integer的数据向基本数据类型int进行转换。如在主程序中,以下语句:
int a = i;
是将类Integer类型的对象赋值给int型变量,此时需要将类Integer类型转换成int类型,这个转换方法由类Integer的operator int (void) const函数完成。
注意:以上类型之间的转换都是隐式转换,可以使用关键字explicit加以禁止。
如将代码写成:
explicit Integer (const int& val) : m_val(val)
{
}
则主程序中的如下语句:
Integer i = 10;
和以下语句:
i = 20;
将变成非法。
12.3 完整代码
本案例的完整代码如下所示:
#include
class Integer
{
private:
int m_val;
public:
Integer (const int& val) : m_val(val)
{
}
int& value (void)
{
return m_val;
}
const int& value (void) const
{
return m_val;
}
operator int (void) const
{
return m_val;
}
};
int main(int argc, const char * argv[])
{
Integer i = 10;
std::cout << i.value() << std::endl;
i = 20;
std::cout << i.value() << std::endl;
int a = i;
std::cout << a << std::endl;
return 0;
}
13 重载new和delete操作符
13.1 问题
由于new和delete都是运算符,所以可以重载new和delete。这样做的好处是,有时希望使用某种特殊的动态内存分配方法。例如,可能有些分配子程序,他们的堆已耗尽,自动开始把一个磁盘文件当虚存储使用,或者用户希望控制某一片存储空间的分配等。
13.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:重载new和delete操作符
代码如下:
#include
class Integer
{
private:
int m_val;
public:
Integer (const int& val = 0) : m_val(val)
{
}
~Integer ()
{
std::cout << “Integer destructor” << std::endl;
}
int& value (void)
{
return m_val;
}
const int& value (void) const
{
return m_val;
}
static void* operator new (size_t size)
{
return malloc(size);
}
static void* operator new[] (size_t size)
{
return malloc(size);
}
static void operator delete (void* p)
{
free§;
}
static void operator delete[] (void* p)
{
free§;
}
};
int main(int argc, const char * argv[])
{
Integer *pi = new Integer(10);
std::cout << pi->value() << std::endl;
delete pi;
Integer *pi1 = new Integer[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
for (int i = 0; i < 10; i++)
std::cout << pi1[i].value() << " ";
std::cout << std::endl;
delete[] pi1;
return 0;
}
上述代码中,以下代码:
static void* operator new (size_t size)
{
return malloc(size);
}
类Integer重载了new操作符。那么当在主程序中,通过如下代码:
Integer *pi = new Integer(10);
使用new创建该类的对象,此时将首先调用该操作符函数分配内存,然后再调用该类的构造函数。
上述代码中,以下代码:
static void operator delete (void* p)
{
free(p);
}
类Integer重载了delete操作符。那么当在主程序中,通过如下代码:
delete pi;
使用delete销毁该类的对象,此时将首先调用该类的析构函数,然后再调用该操作符函数释放内存。
上述代码中,以下代码:
static void* operator new[] (size_t size)
{
return malloc(size);
}
类Integer重载了new[]操作符。那么当在主程序中,通过如下代码:
Integer *pi1 = new Integer[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
使用new[]创建该类的对象数组,此时将首先调用该操作符函数为数组的每个元素分配内存,然后再为数组的每个元素调用该类的构造函数。
上述代码中,以下代码:
static void operator delete[] (void* p)
{
free(p);
}
类Integer重载了delete[]操作符。那么当在主程序中,通过如下代码:
delete[] pi1;
使用delete[]销毁该类的对象数组,此时将首先为数组的每个元素调用该类的析构函数,然后再为数组的每个元素调用该操作符函数释放内存
13.3 完整代码
本案例的完整代码如下所示:
#include
class Integer
{
private:
int m_val;
public:
Integer (const int& val = 0) : m_val(val)
{
}
~Integer ()
{
std::cout << “Integer destructor” << std::endl;
}
int& value (void)
{
return m_val;
}
const int& value (void) const
{
return m_val;
}
static void* operator new (size_t size)
{
return malloc(size);
}
static void* operator new[] (size_t size)
{
return malloc(size);
}
static void operator delete (void* p)
{
free§;
}
static void operator delete[] (void* p)
{
free§;
}
};
int main(int argc, const char * argv[])
{
Integer *pi = new Integer(10);
std::cout << pi->value() << std::endl;
delete pi;
Integer *pi1 = new Integer[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
for (int i = 0; i < 10; i++)
std::cout << pi1[i].value() << " ";
std::cout << std::endl;
delete[] pi1;
return 0;
}