目录
类型转换构造函数
一般构造函数都有如下特点:
- 以对应的类名作为函数名
- 没有返回值
- 可重载(比如有带一个参数的构造函数,也有带两个参数的构造函数等等)
- 有特殊版本(比如拷贝构造函数,移动构造函数)
类型转换构造函数的特点:
- 只带有一个参数
- 要指定转换的方法
- using 格式:using 函数指针类型名 = 函数返回值类型 (*) (形参表);
- 用typedef 格式:typedef 函数返回值类型 (*函数指针类型名) (形参表);
实例:
class testInt
{
public:
//定一个函数指针类型,代表的函数带一个int形参,没有返回值
// typedef void(*point)(int);
using point = void(*)(int);
//这2行的等价的!用其一即可定义一个函数指针类型了!
//义了一个指向无返回值且只有一个int形参的函数的函数指针类型,该函数指针类型名 叫 point
public:
static void myfunc(int v1) //静态成员函数
{
int test = 1;
test = 2;
}
//新的类型转换运算符 把 类类型对象 转换为 函数指针类型
operator point() //const不是必须加
{
return myfunc; //函数地址(函数)作为函数指针类型返回
}
public:
//explicit:禁止隐式类型转换
// explicit testInt(int x):m_i(x)//类型转换构造函数可以看作也是带一个参数的普通构造函数
testInt(int x = 0):m_i(x)
{
if (x < 0) x = 0;
if (x > 100) x = 100;
cout << "类型转换构造函数" << endl;
}
//类型转换运算符
// explicit operator int() const //explicit 禁止隐式的转换
operator int() const
{
cout << "类型转换运算符" << endl;
return m_i;
}
int m_i;
};
//这也是一个普通的带一个参数的构造函数
//这其实就是一个类型转换构造函数,本构造函数可以将一个int数字转换为TestInt类对象
testInt(int x = 0):m_i(x)
{
if (x < 0) x = 0;
if (x > 100) x = 100;
cout << "类型转换构造函数" << endl;
}
TestInt t1 = 22;//✓运行。隐式类型转换:类型转换构造函数帮我们将int数字22转换为TestInt类的对象t1
// testInt te = 12;//调用了类型转换构造和隐式的类型转换
// testInt te1(te); //调用了类型转换构造,但没有隐式的类型转换
// testInt te1(12); //调用了类型转换构造,但没有隐式的类型转换
类型转换运算符(类型转换函数)
与类型转换构造相反:将一个类对象转换为其他的数据类型。
格式:operator type() const
- const是可选项。const表示:在类型转换时不应该改变 待改变类型对象的数据。(你也可以不写上const,只是不写const你的代码安全性不高而已)。
- type:是待转换成(过去)的某种数据类型。
- 该函数是一种特殊的成员函数:没有形参和不能指定返回值类型,但它却能返回一个你所指定的type类型的对象。
testInt ti = 12;
testInt ti1;
ti1 = 12; // 调用了类型转换构造函数 把12的int转换为了testInt类型
int k = ti1 + 2; //调用了operator int() const 隐式调用了转换
//explicit的情况下不能使用,static_cast<int>(ti1)可行
//static_cast静态转换符
int k1 = static_cast<int>(ti1) + 2;
int k2 = ti1.operator int() + 2;//显式转换类型
1.显式的类型转换运算符
在类型转换运算符成员函数 定义前加上explicit关键字。
但是static_cast(expression):是用来do强迫性的隐式类型转换的C++中提供的新式转型关键字
explicit operator int() const {//防止编译器为我们do隐式类型转换
return 110;//按照TestInt类的成员变量m_i是多少,我就转换为对应多少值的int型数字
}
2.有趣范例:类对象转换为函数指针
testInt mytest(12);
// mytest(1);
mytest.operator testInt::point()(1);
(*mytest.operator testInt::point())(11);
调用了operator point() (x)函数后变成了一种函数指针了,且该函数指针是指向静态成员函数static void myfunc(int val)的函数首地址的
类型转换的二义性问题
class CT1
{
public:
CT1(int x)
{
}
};
class CT2
{
public:
CT2(int x)
{
}
};
void testsFun(const CT1 &value)
{
}
void testsFun(const CT2 &value)
{
}
main中调用testsFun(110)//出现二义性问题
编译器觉得,你的代码这么干也行,那么干也行,最后导致编译器都不知道怎么干了的结果,从而编译器就只能给我们报错了。
针对这个二义性问题,我们得出结论:在一个类中,尽量只出现一个类型转换运算符函数。(避免出现二义性问题)
类成员函数指针
是个指针,指向一个类成员函数
class CT
{
public:
void ptfunc(int value)
{
cout << "ptfunc(int value)" << endl;
}
virtual void virtualfunc(int value)
{
cout << "virtualfunc(int value)" << endl;
}
static void staticfunc(int value)
{
cout << "staticfunc(int value)" << endl;
}
};
1.指向普通成员函数的指针
声明格式: “类名::*函数指针变量名”
- 用“&类名::成员函数名”可获取类的普通成员函数的地址。(这里是一个真正的“内存地址”)。
- 用“(类对象名.*函数指针变量名)(形参表) ” 或“(类对象指针->*函数指针变量名)(形参表)” 可调用所指向的类的普通成员函数。
// 定义一个普通的类成员函数指针
void (CT::*myPoint)(int);//一个类成员函数指针变量(myPoint)的定义
myPoint = &CT::ptfunc; //类成员函数指针变量被赋值
//大家注意:成员函数属于类的,不属于类对象,只要有类在就有成员函数地址在
//但是你若使用这个成员函数的指针,必须把它绑定到一个类对象才能使用
//使用格式: 类对象名.*函数指针变量名或者指针->函数指针变量名
CT ct,*pct;
pct = &ct;//指针需要初始化
(ct.*myPoint)(100);
(pct->*myPoint)(100);
2.指向virtual虚函数的指针
声明格式: “类名::*函数指针变量名”
- 用“&类名::成员函数名”可获取类的虚函数的地址。(这里是一个真正的“内存地址”)
2. 用“(类对象名.*函数指针变量名)(形参表) ” 或“(类对象指针->*函数指针变量名)(形参表)” 可调用所指向的类的虚成员函数。
void (CT::*myvirtualfunc)(int);
myvirtualfunc = &CT::virtualfunc;
CT ct,*pct;
pct = &ct;
(ct.*myvirtualfunc)(100);
(pct->*myvirtualfunc)(100);
3.指向静态static成员函数的指针
声明格式: “*函数指针变量名”
- 用“&类名::成员函数名”可获取类的static静态成员函数的地址。(这里是一个真正的“内存地址”)
- 用“(*函数指针变量名)(形参表) ” 或“函数指针变量名(形参表)” 可调用所指向的类的static静态成员函数。(这与之前的指向普通成员函数的指针or指向虚函数的指针必须通过对象or指向对象的指针来调用对应的成员函数是不同的哈!)
void (*stf)(int) = &CT::staticfunc;
stf(1);
(*stf)(1);
类成员变量指针
1.指向普通成员变量的指针
声明格式:“变量类型 类名::*成员变量指针名 = &类名::成员变量名;”(这里并不是一个真正的“内存地址”,而只是该成员变量与对应的类对象之间的一个偏移量offset。用“对象名.*成员变量指针名”或“对象指针->*成员变量指针名”来读写对应的普通成员变量。
class CT {
public:
int m_a;//普通成员变量,属于(依附于)类的对象
CT() {}
~CT() {
cout << "调用了类CT的析构函数!" << endl;
}
};
//定义一个指向CT类的成员变量m_a的成员变量指针pm_a
int CT::*pm_a = &CT::m_a;//0x0000000000000008(???)
//注意:指向类的成员变量的指针并不是一个真正意义上的地址值!
//也是该成员变量与该类的对象之间的偏移量offset
//只有当指向成员变量的指针依附在类的对象上时,才会让该成员变量产生真正的内存地址值!
int main(void) {
CT ct;
CT* pct = new CT;
ct.*pm_a = 1234;//通过调用指向成员变量m_a的指针来修改对象ct.m_a的值!
//<===>ct.m_a = 1234;
cout << "ct.m_a = " << ct.m_a << endl;
ct.m_a = 5678;
cout << "ct.m_a = " << ct.m_a << endl;
pct->*pm_a = 78;
cout << "ct.m_a = " << pct->m_a << endl;
pct->*pm_a = 88;
cout << "ct.m_a = " << pct->m_a << endl;
delete pct;
return 0;
}
2.指向静态成员变量的指针
声明格式:“变量类型 *成员变量指针名 = &类名::static成员变量名;”(这里是一个真正的“内存地址”)。用“*成员变量指针名”来读写对应的普通成员变量。
class CT {
public:
int m_a;//普通成员变量,属于(依附于)类的对象
static double m_b;//类内声明一个静态成员变量,属于(依附于)类,属于任何对象所共享的
CT() {}
~CT() {
cout << "调用了类CT的析构函数!" << endl;
}
};
double CT::m_b = 1.88;//类外定义(初始化)了一个静态成员变量m_b
//定义一个指向CT类的静态成员变量m_b的成员变量指针pm_b
double* pm_b = &CT::m_b;
//&pm_b 0x00007ff69848f088 说明这种指向static静态成员变量的指针是有真正的地址值的!
int main(void) {
*pm_b = 138.8;//<===> CT::m_b = 138.8
cout << "CT::m_b = " << CT::m_b << endl;
*pm_b = 998.8;//<===> CT::m_b = 998.8
cout << "CT::m_b = " << CT::m_b << endl;
return 0;
}