C++基础学习-27类型转换构造函数、运算符、类成员指针之详述

类型转换构造函数

一般构造函数都有如下特点:

  1. 以对应的类名作为函数名
  2. 没有返回值
  3. 可重载(比如有带一个参数的构造函数,也有带两个参数的构造函数等等)
  4. 有特殊版本(比如拷贝构造函数,移动构造函数)

类型转换构造函数的特点:

  1. 只带有一个参数
  2. 要指定转换的方法
  3. using 格式:using 函数指针类型名 = 函数返回值类型 (*) (形参表);
  4. 用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

  1. const是可选项。const表示:在类型转换时不应该改变 待改变类型对象的数据。(你也可以不写上const,只是不写const你的代码安全性不高而已)。
  2. type:是待转换成(过去)的某种数据类型。
  3. 该函数是一种特殊的成员函数:没有形参和不能指定返回值类型,但它却能返回一个你所指定的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.指向普通成员函数的指针

声明格式: “类名::*函数指针变量名”

  1. 用“&类名::成员函数名”可获取类的普通成员函数的地址。(这里是一个真正的“内存地址”)。
  2. 用“(类对象名.*函数指针变量名)(形参表) ” 或“(类对象指针->*函数指针变量名)(形参表)” 可调用所指向的类的普通成员函数。
    // 定义一个普通的类成员函数指针
    void (CT::*myPoint)(int);//一个类成员函数指针变量(myPoint)的定义
    myPoint = &CT::ptfunc; //类成员函数指针变量被赋值
    
    //大家注意:成员函数属于类的,不属于类对象,只要有类在就有成员函数地址在
    //但是你若使用这个成员函数的指针,必须把它绑定到一个类对象才能使用
    //使用格式:  类对象名.*函数指针变量名或者指针->函数指针变量名
    
    CT ct,*pct;
    pct = &ct;//指针需要初始化
    (ct.*myPoint)(100);
    (pct->*myPoint)(100);

2.指向virtual虚函数的指针

声明格式: “类名::*函数指针变量名”

  1. 用“&类名::成员函数名”可获取类的虚函数的地址。(这里是一个真正的“内存地址”)
    2. 用“(类对象名.*函数指针变量名)(形参表) ” 或“(类对象指针->*函数指针变量名)(形参表)” 可调用所指向的类的虚成员函数。
    void (CT::*myvirtualfunc)(int);
    myvirtualfunc = &CT::virtualfunc;
    CT ct,*pct;
    pct = &ct;
    (ct.*myvirtualfunc)(100);
    (pct->*myvirtualfunc)(100);

3.指向静态static成员函数的指针

声明格式: “*函数指针变量名”

  1. 用“&类名::成员函数名”可获取类的static静态成员函数的地址。(这里是一个真正的“内存地址”)
  2. 用“(*函数指针变量名)(形参表) ” 或“函数指针变量名(形参表)” 可调用所指向的类的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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值