既然说是浅谈C++之面向程序设计,那么本次我会和大家分享面向对象设计中经常使用的几种类型:1.conversion function(转换函数)、2.non-explicit-one-argument-ctor、3.explicit-one-argument-ctor、4.pointer-like class(智能指针)、5.Function-like class(仿函数)、6.namespace(命名空间)、7.class template(类模板)、8.Function template(函数模板)、9.member template(成员模板)、10.specialization(模板特化)、11.partial specialization(模板偏特化)、12.template template parameter (模板模板参数)、13.关于C++标准库、14.variadic template(since C++11)、15.reference、16.复合&继承关系下的构造函数和析构函数。
下面对上述列出的C++小技巧进行展开说明:
注:本次文章将前3中小技巧作为本次《浅谈C++之面向对象程序设计的几种小技巧》系列的第一部分进行说明,对于剩下的其他十三种小技巧请参照《浅谈C++之面向对象程序设计的几种小技巧》系列的后续的文章。谢谢!
- *** 1.conversion function (转换函数):***
我们知道class(类)是C++中重要的一个机制,对于我所说的转换函数,在C++中往往有两种形式,一种是将:class(类)转出去;另一种是将外界的东西转化成class(类)。对于我说的这两种大家可能不是太明白,下面结合程序做进一步的说明:
#include<iostream>
using namespace std;
//如下:将class转出去,通过利用operator将class(类)原本的类型转化成另一种类型
class Fraction{
public:
Fraction(int num, int den = 1) :
m_num(num), m_den(den){}
operator double()const{ //将数据类型为的 Fraction 的数据转化成 double 类型数据
return static_cast<double>(m_num)/ static_cast<double>(m_den);
}
private:
int m_num;
int m_den;
};
int main()
{
Fraction f(3, 5);//对象f
double d = 3+f;//编译器将自动将对象f转化成double型
return 0;
}
上述类Fraction之所以会转化成double类型,是因为在类Fraction中编译器自动自动调用类Fraction中的转化函数operator double() const{}实现函数转化(将class转出去)。
对于重载函数(转化函数)operator double() const{},其中double()表示Fraction类转化后的类型(转化后为double型),小括号里面无参数;返回类型不需要要写(因为在转化前并不知道会将其转化成什么类型,如果贸然写上返回类型,容易在写返回类型时将类型写错,从而造成编译的错误,那么干脆不写,因为返回类型就是转化后的类型。
同时大家可能会注意到,重载函数(转化函数)中的const,之所以在函数后面加const是因为该函数体在执行的过程中不会改变函数体中的参数值。也就是说在类中被const声明的成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查.所以加上const。那么有的人可能会问,如果不加可不可以。我的回答是可以,但是最好加上,而且对于有些程序来说,不加const的话有可能会造成不可预知的程序错误。类中的const成员函数的目的是为了指明哪个成员函数可以在const对象上被调用。
通过上述我们知道了函数转化的一种方法:将class转出去。如果写的时候认为合理的话,可以给类写好几个转化函数。刚才说了,对于转化函数有两种,上述只是介绍了其中的一种,并且我们平时说的转化函数也往往就是指这一种,而另一种转化函数——将别的东西转化成这种类(class)就是下面要给大家介绍的non-explicit-one-argument-ctor。
- *** 2.non-explicit-one-argument-ctor:***
通过名字可以知道,这是一种只有的一个实参的构造函数,并且构造函数前面不加explicit。
对于C++中的构造函数前面加关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生,说明构造器只能被明确的调用。申明为explicit的Ctor(构造函数)不能在隐式转换中使用。C++中,一个参数的构造函数,承担了两个角色。a.构造函数;b.是个默认且隐含的类型转换操作符。
介绍完explicit的作用后,我们言归正传继续介绍non-explicit-one-argument-cotr这种类型的转化(将外界其他类型的东西转化成class(类)的类型。同样我们还是通过一段程序来进行说明。
#include<iostream>
using namespace std;
//如下:将class转出去,通过利用operator将class(类)原本的类型转化成另一种类型
class Fraction{
public:
Fraction(int num, int den = 1) :
m_num(num), m_den(den){}//此构造函数有一个实参den=1
Fraction operator + (const Fraction & f){ //将数据类型为的 Fraction 的数据转化成 double 类型数据
return Fraction((this->m_num)+(f.m_num)*(this->m_den),this->m_den);
}
int getNum()const{ return m_num; }
int getDen()const{ return m_den; }
private:
int m_num;
int m_den;
};
int main()
{
Fraction f(3, 5);
Fraction d = f+3;
cout << "(" << d.getNum() << "," << d.getDen() << ")"<<endl;
return 0;
}
上述程序的输出结果为:d=(18,5)
从上述的程序可以看出,这是一种隐式转换,将 Fraction d = f+3;中的3通过构造函数进行隐式转化成(3,1)其类型为 Fraction 类型。再通过运算符(“+”)进行重载,完成 Fraction 类的对象相加的操作。
通过上述程序,相信已经可以窥探到:non-explicit-one-argument-cotr 的奥义了。同时也会知道explicit关键字在构造函数中的作用。为了加深对explicit这个关键字的理解,下面我们在构造函数的前面加上explicit进行比对,也就是我接下来要说的explicit-one-argument-ctor……
- 3.explicit-one-argument-ctor:
对于将class(类)转出去[即所说的conversion function (转换函数)],以及将外界其他类型的东西转化成class(类)的类型[即所说的non-explicit-one-argument-ctor]。那么如果给ctor(构造函数)加上explicit后会如何呢。
下面我们介绍在ctor(构造函数)前面加上关键字explicit后会如何,通过关键字的名字————explicit可以知道,加上这个关键字之后,构造函数将只能通过显式调用,而不能像上面我么你说的第二种情况进行隐式调用。
为了将其说的更清晰,还是选择通过一段程序来进行讲解。
#include<iostream>
using namespace std;
//如下:将class转出去,通过利用operator将class(类)原本的类型转化成另一种类型
class Fraction{
public:
explicit Fraction(int num, int den = 1) ://将构造函数前面加上关键字explicit后,该构造函数将变成只能通过显示调用。
m_num(num), m_den(den){}
operator double()const{ //将数据类型为的 Fraction 的数据转化成 double 类型数据
return static_cast<double>(m_num) / static_cast<double>(m_den);
}
Fraction operator + (const Fraction & f){ //将数据类型为的 Fraction 的数据转化成 double 类型数据
return Fraction((this->m_num) + (f.m_num)*(this->m_den), this->m_den);
}
private:
int m_num;
int m_den;
};
int main()
{
Fraction f(3, 5);
Fraction d1 = f + 4; //Error C2440:"初始化“:无法从”double“转换为Fraction”
double d = 3 + f;
cout << d << endl;
return 0;
}
上面的这段程序编译时将会出现错误,且它的错误提示为 Error C2440:"初始化“:无法从”double“转换为Fraction”。这时候需要思考程序为什么会出现这种错误的提示:因为在构造函数的前面加上了explicit,使构造函数:
explicit Fraction(int num, int den = 1) :
m_num(num), m_den(den){}
只能通过显式进行调用,而不能进行隐式调用(即不能够通过构造函数将数字“4”转换成Fraction(4,1),也就是说当构造函数前面加上了关键字explicit后,这种隐式的转化是不被允许的。
以上部分就是本次文章《浅谈C++之面向对象程序设计的几种小技巧》的第一部分的全部内容,对于其他的三种小技巧,请见《浅谈C++之面向对象程序设计的几种小技巧》系类的后续文章。谢谢!