类型转换构造函数
我们通过 类型转换构造函数 和 类型转换运算符 来做到 类型转换。
构造函数:移动构造函数,拷贝构造函数,默认构造函数、普通构造函数
特点:
1、以类名作为函数名
2、没有返回值;
类型转换构造函数特点
有一种构造函数叫“类型转换构造函数”,主要能力是:它可以将某个其他的数据转换成该类类型的对象。特点:
1、只有一个参数,该参数又不是本类的const引用。(const &A),该参数其实就是带转换的数据类型。所以显然带转换的数据类型都不应该是本类类型。
2、在类型转换构造函数中我们要制定转换的方法(在这个函数中要干什么);
class TypeInt{
public:
int m_int;
public:
//explicit TypeInt(const int &i = 0):m_int(x) explicit可以禁止做隐式类型转换
TypeInt(const int &i = 0):m_int(x)
{
if (m_int < 0) m_int = 0;
if (m_int > 100) m_int = 100;
}
};
int main(){
TypeInt ti = 12; //隐式类型转换,将数字12转换成了TypeInt对象
//编译器用12这个数字通过调用typeInt类的类型构造函数来创建一个
//临时对象,并把这个对象构造到了ti的预留空间
TypeInt ti2(22); //这里调用了类型转换构造函数,但是没有进行隐式类型转换
TypeInt ti3;
ti3 = 6; //这里先将6调用类转换构造函数将6转换成TypeInt类型对象,然后调用赋值运算
//符将这个对象给ti3,这里我们没有定义拷贝赋值运算符,所以系统自动生成了
//一个默认的拷贝赋值运算符
}
类型转换构造函数也是一个带有一个参数的普通构造函数。
类型转换运算符(类型转换函数)
能力和类型转换构造函数相反。他能够将一个类类型对象 转成 某个其他数据类型,
格式:
operator type() const
1、const是可选项,const表示一般不应该改变带转换对象内容,但不是必须有const
2、type:表示要转换成的某种类型。只要能够作为函数返回类型都可以;数组指针,函数指针,引用都可以
3、类型转换运算符没有形参,因为类型转换运算符都是隐式执行的,所以,根本没法给它传递参数。同事 ,也不能指定返回类型,但是它却能返回type指定的值。
4、必须定义为类的成员函数。
class TypeInt{
public:
int m_int;
public:
//explicit TypeInt(const int &i = 0):m_int(x) explicit可以禁止做隐式类型转换
TypeInt(const int &i = 0):m_int(x)
{
if (m_int < 0) m_int = 0;
if (m_int > 100) m_int = 100;
}
//explicit operator int() const //防止编译器做隐式类型转换
operator int() const
{
return m_int;
}
};
int main(){
ti3 = 6;
int k = ti3 + 5; //隐式类型转换。此处会调用类型转换运算符将ti3转换成了int,在与5
//做加法
//加explicit后,此处会报错
int k2 = ti3.operator int() + 5; //显示调用。没有形参,所以()为空
int k3 = static_cast<int>(ti3) + 5; //这种方式也会调用类型转换运算符
}
指针类型转换
class TypeInt{
//typedef void(*tfpoint)(int); 定义函数指针
using tfpoint = void(*)(int);
public:
int m_int;
public:
void myfunc(int v){
int test;
test = 1;
}
TypeInt(const int &i):m_int(i) {};
operator tfpoint() //const 不是必须加
{
return myfunc;
}
};
int main(){
TypeInt myi(12);
myi(123); //看起来是个可调用对象的感觉。相当于调用了两个函数:1、类型转换运算符转换成函数指针类型 2、通过函数指针调用具体的函数
myi.operator TypeInt::tfpoint()(123); //第一个圆括号之前表示类的指针类型转换函
//数,返回了myfunc函数,第二个括号表示表
//示给myfunc函数的参数123
}
类型转换的二义性问题
二义性:这么招可以,那么招也行,所以只能报错。
建议:在一个类中,尽量只出现一个类型转换运算符。
class CT1{
public:
CT1(int x){}
};
class CT2{
public:
CT2(int x){}
};
void testfunc(const CT1 &C) {}
void testfunc(const CT2 &C) {}
int main(){
testfunc(100);//报错,会造成二义性,因为100是int类型可以转换成CT1对象也可以转换成
//CT2对象
testfunc(CT1(100)); //可以,意味着设计缺陷
}
类成员函数指针
格式:类名::*函数指针变量名 来声明普通成员函数指针
用 &类名::成员函数名 来获取成员函数地址
class CT{
public:
void ptfunc(int tmpvalue) {cout << "ptfunc" << endl;}
virtual virtualfunc(int tmpvalue) {cout << "virtualfunc" << endl;}
static staticfunc(int tmpvalue) {cout << "staticfunc" << endl;}
};
int main(){
void (CT::*myfuncPoint)(int);
myfuncPoint = &CT::ptfunc; //类成员函数指针变量myfuncPoint被赋值,这是个真正的
//内存地址
CT ct, *pct;
pct = &ct;
(ct.*myfuncPoint)(100);
(pct->*myfuncPoint)(200);
}
注意:成员函数是属于类的,不是属于类对象的,只要有类在就有成员函数地址在。但是你若要使用这个成员函数指针就必须把它绑定到一个类对象上才能调用。
使用函数指针的格式是:类对象名.*函数指针变量名,如果是个指针对象则调用格式是:指针名->*函数指针变量名,普通的成员函数地址是一个真正的地址
对于虚函数
与普通函数一样
void (CT::*myfuncPointVirtual)(int);
myfuncPointVirtual= &CT::virtualfunc; //类成员函数指针变量myfuncPoint被赋值,也是
//真正的地址
CT ct, *pct;
pct = &ct;
(ct.*myfuncPointVirtual)(100);
(pct->*myfuncPointVirtual)(200);
对于静态成员函数
使用 “*函数指针变量名” 来声明静态成员函数指针,使用“&类名::成员函数名”来获取类成员函数地址。这个也是真正的地址定义一个静态的类成员函数指针并赋值
void (*myfuncPointStatic)(int);
myfuncPointStatic = &CT::staticfunc; //类成员函数指针变量myfuncPoint被赋值,也是
//真正的地址
myfuncPointVirtual(100); //直接使用静态成员函数指针即可调用
类成员变量指针
class A{
public:
int m_a;
static int m_statica;
};
int main(){
int A::*mp = &A::m_a; //并不是真正意义上的指针,不是执行内存中的某个地址,而是该
//成员变量与该类对象之间的偏移量
A a;
a.*mp = 189;
int *msta = &A::m_statica;
*msta = 798; //等价于 CT::m_sta = 122
}
普通类成员变量指针不是真正的指针而是成员变量与该类对象之间的偏移量
当一个类中存在虚函数时,编译器就会给这个类生成一个虚函数表(属于类的),当生成对象时这个类中有虚函数表,那么对象中就会有一个虚函数表指针,这个指针占有4个字节。