C++ Primer读书笔记 (1)
C++ Primer读书笔记 (1)
7. 类
1.构造函数
构造函数的名字和类名相同
template<typename T> Class CComlpex{
private:
T realVal;
T imagVal;
public:
CComplex():realVal(0),imagVal(0) {}
CComplex(const T rv, const T iv):
realVal(rv), imagVal(iv) {}
};
尽量使用参数列表形式的成员初始化,这样可以显式地调用成员变量的构造函数。虽然当成员函数是基本类型的时候,赋值初始化与直接初始化时等价的。
当成员函数定义在类的内部的时候,成员函数是隐式inline(内联)的。
不带参数或者可以不提供参数(例如为所有参数都提供了默认值)的构造函数为默认构造函数。
CComplex<double> z;
调用默认构造函数。注意,如果不考虑模板,CComplex z();也不是调用默认构造函数,而是声明了默认构造函数,不能起到创建对象的作用。
如果不显式提供,编译器会隐式合成一个默认构造函数,即调用每个成员的默认构造函数。如果有其他构造函数,编译器不再合成默认构造函数,此时对默认构造函数的隐式调用将报错。
2.类拷贝
一个以自身类型的const引用为参数的构造函数为拷贝构造函数
CComplex(const CComplex &z)
{
realVal = z.realVal;
imagVal = z.imagVal;
}
当执行赋值初始化的时候,系统会调用拷贝构造函数,例如
CComplex<double> z1(1.0,1.0);
CComplex<double> z2 = z1; // 调用拷贝构造函数
如果不显式提供,则系统将隐式合成一个拷贝构造函数。隐式的拷贝构造函数只能实现所有的成员的直接拷贝(即调用它们的拷贝构造函数),当对于基本类型的时候,这是没有问题的。但是有些情况会出现问题。
3.隐式转换
当一个构造函数只有一个参量的时候,可以用于隐式类型转换,例如
CComplex(const T z)
{
realVal = z;
imag = 0;
}
此时可以利用该构造函数进行隐式转换,注意隐式类型转换只能使用一步。
CComplex<double> z = 1.0; //double隐式转换成临时的CComlpex<double>,再赋给z
在向CComplex<T>类型(或对应的const、引用、const引用)的形参传递T型的实参时,以可以发生上述隐式类型转换。
如果想禁止隐式类型转换,可以用explicit来修饰构造函数
explicit CComplex(const string &str);
此时不可以使用隐式转换,但可以使用static_cast等显式类型转换。
string str = "1+1i";
CComplex<double> = str; //Error!
CComplex<double> = static_cast<CComplex<double>>(str); //OK!
4.赋值
重载operator=运算符可以定义类的赋值函数,用于类之间的赋值,例如
CComplex<T>& operator=(const CComplex<T> &z) //operator= 应该定义成成员函数
{
realVal = z.realVal;
imagVal = z.imagVal;
return *this;
}
当发生赋值的时候,编译器将调用赋值函数,例如
CComplex<double> z1(1,1);
CComplex<double> z2 = z1; //调用拷贝构造函数
CComplex<double> z3; //调用默认构造函数,创建空的z3
z3 = z1; //调用复制函数,即重载的operator=
注意区分赋值函数和拷贝构造函数,后者仅在初始化的时候用,前者在对已经定义过的实例赋值的时候用。
5.运算符重载
重载operator后面加运算符的函数可以实现运算符重载。单目运算符重载函数有一个形参,表示其操作数;双目运算符重载函数有两个形参,第一个形参和第二个形参分别表示其左操作数和右操作数。例如当不考虑template的情况下,对CComplex类的==运算符进行重载
bool operator==(const CComplex &lhs, const CComplex &rhs)
{
return (lhs.realVal == rhs.realVal) &&
(lhs.imagVal == rhs.imagVal);
}
这里面有一个问题,因为上述operator==函数不是CComplex的成员函数,因此不能访问private对象realVal和imagVal。解决的方法有两种,一是将operator==定义成成员函数
bool CComplex::operator==(const CComplex &rhs)
{
return (realVal == rhs.realVal) &&
(imagVal == rhs.imagVal);
}
注意当operator==被定义成成员函数之后,它的左操作数形参省略不写,唯一的参量是其右操作数参量。
另一种解决方法是在类中将operator==声明为CComplex类的友元函数,方法如下
class CComplex
{
friend bool operator==(const CComplex&, const CComplex&);
private:
//...
};
6.友元
在类的内部声明一个可以声明一个类、函数、类的成员函数为该类的友元。此时友元类的成员函数、友元函数可以访问该类的私有对象,例如
class CComplex
{
friend bool operator==(const CComplex&, const CComplex&);
private:
double realVal;
double imagVal;
};
bool operator==(const CComplex&,const CComplex&)
{
return (lhs.realVal == rhs.realVal) &&
(lhs.imagVal == rhs.imagVal);
}// operator==是CComplex的友元函数,可以访问CComplex的private成员
对类进行运算符重载的时候,既可以声明成成员函数,也可以声明成友元函数。但是值得注意的是,成员函数必选满足所重载的运算符的左操作数是类类型,例如operator<<的左操作数类型是ostream&,不是类类型,因此不能声明成成员函数,只能声明成友元函数。
一般来说,单目运算符通常被声明成成员函数,双目运算符被声明成友元函数。
7.模板
使用template<typename T>可以定义一个类型模板,例如
template<typename T> class CComplex
{
private:
T realVal;
T imagVal;
};
是一个模板类。该类实例化的时候,要显式地给出模板参数列表,例如
CComplex<double> z;
又例如
template<typename T> bool isEqual(const CComplex<T>&, const CComplex<T>&);
是一个模板函数,在调用的过程中
CComplex<double> z1(1,1);
CComplex<double> z2(2,-2);
bool res = isEqual(z1,z2); //让模板函数通过函数实参推断模板类型参数
bool res2 = isEqual<>(z1,z2); //同上,通过一个空的参数列表显式指出isEqual是一个模板函数
bool res3 = isEqual<double>(z1,z2);//直接给出模板类型参数,不通过函数实参推断
注意,模板函数的声明和定义必须放在同一个文件中,如下的形式会被编译报错
// add.h
template<typename T> bool isEqual(const CComplex<T>&, const CComplex<T>&);
// add.cpp
template<typename T> bool isEqual(const CComplex<T>& z1, const CComplex<T>& z2)
{
//...
}
因此模板函数的定义通常都放在.h文件中。
当把模板函数声明为友元函数的时候,要在类前先定义模板函数,并且在友元函数声明的过程中,要通过空的参数列表<>来显式指定这是一个模板函数。
template<typename T> class CComplex; //声明类,供模板函数声明用
template<typename T> CComplex<T> operator+(const CComplex<T>&, const CComplex<T>&); //声明模板函数
template<typename T> class CComplex //定义类
{
friend CComplex<T> operator+<>(const CComplex<T>&,const CComplex<T>&);
//声明友元函数,并显式地使用<>表明这是一个模板函数
private:
//...
};
template<typename T> CComplex<T> operator+(const CComplex<T>&, const CComplex<T>&) //定义模板函数
{
//...
}
对于模板函数是运算符重载函数的时候,这种方法尤为必要。但是,可以把模板函数声明成成员函数来简化问题,或者可以声明成非成员非友元函数,但是调用其他友元函数就可以了,例如非成员非友元的operator+调用成员函数operator+=。

浙公网安备 33010602011771号