C++进阶剖析( 十 八)之其他操作符

本文深入探讨C++中的逻辑操作符、逗号操作符、类型转换等核心概念,包括操作符的原生意义、重载规则及类型转换机制,通过实例解析操作符的工作原理和潜在陷阱。

1.1 逻辑操作符
1.1.1 逻辑运算符的原生意义

  • 操作数只用两种值(true 和 false)
  • 逻辑表达式不用完全计算就能确定最终值(短路规则)
  • 最终结果只能是true或者 false

1.1.2 原生语义举例

int func(int i)
{
	cout<<"int func(int i) : i= "<< i<<endl;
	return i;
}

int main()
{  
	int a = 0;
	int b = 1;
	//if(a&&b)
	if(func(0) &&func(1))
	{
		cout<<"result is true"<<endl;
	}
	else{
		cout<<"result is false"<<endl;
	}
	cout<< endl;

	if(func(0) ||func(1))
	{
		cout<<"result is true"<<endl;
	}
	else{
		cout<<"result is false"<<endl;
	}

	return 0;
}
  • 注意分析结果,以及实验的目的
    在这里插入图片描述

1.1.3 逻辑操作符是否可以重载

  • 逻辑操作符可以重载吗?
  • 重载逻辑操作符有什么意义?
using namespace  std;

class Test{
private:
	int i;
public:
	Test(int i)
	{
		this->i =i;
	}
	int value() const
	{
		return i;
	}

};
bool operator&&(const Test&obj1,const Test&obj2)  //&&重载
{
	 return (obj1.value() && obj2.value());
}

bool operator||(const Test&obj1,const Test&obj2)   // ||重载
{
	return (obj1.value() || obj2.value());
}



Test func(Test i)
{
	cout<<"Test func(Test i) : i.value()= "<< i.value()<<endl;
	return i;
}

int main()
{  

	Test t0(0);
	Test t1(1);
	if(func(t0) &&func(t1))
	{
		cout<<"result is true"<<endl;
	}
	else{
		cout<<"result is false"<<endl;
	}
	cout<< endl;

	if(func(t1) ||func(t0))
	{
		cout<<"result is true"<<endl;
	}
	else{
		cout<<"result is false"<<endl;
	}

	return 0;
}
  • 结果
    在这里插入图片描述
    1.1.4 问题本质
  • C++通过函数调用扩展操作符的功能
  • 进入函数体前必须完成所有参数的计算
  • 函数参数的计算次序是不定的
  • 短路法则完全失效

1.1.5 建议

  • 实际工程中开发中避免虫子逻辑操作符
  • 通过重载比较操作符代替逻辑操作符重载
  • 直接使用成员函数代替逻辑操作符重载
  • 使用全局函数对逻辑操作符进场重载

1.2 逗号操作符

1.2.1逗号操作(,)
逗号操作(,)可以构成逗号表达式

  • 逗号表达式用于将多个子表达式连接为一个表达式
  • 逗号表达式的值为最后一个子表达式的值
  • 逗号表达式中的前 N-1个子表达式可以没有返回值
  • 逗号表达式按照从左向右的顺序计算每个子表达式的值
    • exp1,exp2,exp3,…,expN

1.2.2 案例

  • 案例1
using namespace  std;

void func(int i)
{
	cout<<"func() i ="<<i <<endl;
}

int main()
{  
	int a[3][3] = {
		(1,2,3),
		(4,5,6),
		(7,8,9)
	};
	int i =0;
	int j =0;
	while(i<5)
		func(i),
	i++;
	
	cout<< endl;
	cout<<"======================================"<<endl;
	for( i =0;i<3;i++)
	{
		for(j=0;j<3;j++)
		{
			cout<< a[i][j]<<endl;
		}
	}
	cout<< endl;
	cout<<"======================================"<<endl;
	(i,j)=10;  //dot 
	cout<<"i= "<<i <<endl;
	cout<<"j= "<<j <<endl;

	return 0;
}
  • 结果:
    在这里插入图片描述
  • 案例2
void func(int i)
{
	cout<<"func() i ="<<i <<endl;
}

int main()
{  
	int a[3][3] = {
		{1,2,3},
		{4,5,6},
		{7,8,9}
	};
	int i =0;
	int j =0;
	while(i<5)
	{
		func(i);
		i++;
	}
	cout<< endl;
	cout<<"======================================"<<endl;
	for( i =0;i<3;i++)
	{
		for(j=0;j<3;j++)
		{
			cout<< a[i][j]<<endl;
		}
	}
	cout<< endl;
	cout<<"======================================"<<endl;
	(i,j)=10;  //dot 
	cout<<"i= "<<i <<endl;
	cout<<"j= "<<j <<endl;

	return 0;
}
  • 结果
    在这里插入图片描述

1.2.3 重载逗号操作符

  • 在C++中重载逗号操作符时合法的
  • 使用全局函数对逗号操作符进行重载
  • 重载函数的参数必须有一个是类类型
  • 重载函数的返回值类型必须是引用
class& operator,(const class&a,const class&b)
{
	return const_cast<class&>(b);  //这里我们返回的是第二个参数,对应“,”表达式返回值是最后一个函数
}

1.2.4 示例代码

class Test
{
private:
	int mvalue;
public:
	Test(int value)
	{
		this->mvalue = value;
	}
	int value()
	{
		return mvalue;
	}
};

Test& operator,(const Test&obj1,const Test&obj2)
{
	return const_cast<Test&>(obj2);
}

Test func(Test&i)
{
	cout <<"Test func(Test&i) : i = "<<i.value()<<endl;
	return i;
}
int main()
{  
	Test t1(1);
	Test t2(2);
	Test tt = (func(t1),func(t2));
	cout<< tt.value() <<endl;

	return 0;
}
  • 结果
    在这里插入图片描述
class Test
{
private:
	int mvalue;
public:
	Test(int value)
	{
		this->mvalue = value;
	}
	int value()
	{
		return mvalue;
	}
};

/*
Test& operator,(const Test&obj1,const Test&obj2)
{
	return const_cast<Test&>(obj2);
}
*/
Test func(Test&i)
{
	cout <<"Test func(Test&i) : i = "<<i.value()<<endl;
	return i;
}
int main()
{  
	Test t1(1);
	Test t2(2);
	Test tt = (func(t1),func(t2));
	cout<< tt.value() <<endl;

	return 0;
}

在这里插入图片描述

1.2.5 问题的本质分析

  1. C++通过函数调用扩展操作符的功能
  2. 进入函数体前必须完成所有参数的计算
  3. 函数参数的计算次序是不定的
  4. 重载后无法严格从左向右计算表达式
  • 注意: 实际中是完全没有必要重载逗号操作符的
    1.3 前置操作符和后置操作符
    1.3.1
    1.3.2
    1.3.3

1.4 类型转换函数上
1.4.1标准数据类型转换

  • 标准数据类型之间会进行隐式类型安全转换(小类型转换成大类型)
  • 转换规则如下:
    在这里插入图片描述
  • 举例
short  s ='a';
cout<<"sizeof(s + 'b')  =  "<< sizeof(s+'b')<<endl;  //结果是4 ,意外不?

1.4.2 思考

  • 普通类型和类类型之间能否进行类型转换
  • 类类型之间能否进行类型转换?

1.4.3 再论构造函数

  • 构造函数可以定义不同类型的参数
  • 参数满足下列条件时成为转换构造函数
    1. 有且仅有一个参数
    2. 参数是基本类型
    3. 参数是其他类类型

1.4.4 旧式的C语言强制类型转换

  • 编译器尽力尝试的结果是隐式类型
  • 隐式类型转换
    • 会让程序以意想不到的方式进行工作
    • 是工程中bug的重要来源
class Test
{
private:
	int mvalue;
public:
	Test()
	{
		mvalue =0;
	}
	Test(int value)
	{
		this->mvalue = value;
	}
	Test operator+(const Test&obj)
	{
		Test ret(mvalue + obj.mvalue);
		return ret;
	}
	int value()
	{
		return mvalue;
	}
};
int main()
{  
	Test t;
	t = 4 ;   // <====> t = Test(4); 
	cout<<"t.value = "<<t.value()<<endl;
	cout<<endl;
	cout<<"====================================="<<endl;
	Test r;
	r =t + 10;  // Test operator+(const Test&obj) 
				// r  =t + Test(10);   编译器自动进行了隐式类型转换,导致bug的出现
	cout<<"r.value = "<<r.value()<<endl;
	return 0;
}

在这里插入图片描述

1.4.5 避免自动转换方法

  • 工程中通过explicit关键字杜绝编译器的转换尝试
  • 转换构造函数被explicit修饰时只能进行显示转换
  • 转换方式
    • static_cast (value)
    • ClassName(value)
    • (ClassName)(value); //不推荐

1.5 类型转换函数下
1.5.1 问题

  • 类类型能否类型转换成普通类型?不能直接转换
    1.5.2 定义类型转换函数

  • 案例1

class Test
{
private:
	int mvalue;
public:
	Test(int value =0)
	{
		this->mvalue = value;
	}
	
	int value()
	{
		return mvalue;
	}

};
int main()
{  
	Test t(10);
	
	int i =t;
	return 0;
}

在这里插入图片描述

  • 案例2 //注意比较两个案例的区别
class Test
{
private:
	int mvalue;
public:
	Test(int value =0)
	{
		this->mvalue = value;
	}
	
	int value()
	{
		return mvalue;
	}
	operator int()   
	{
		return mvalue;
	}
};

int main()
{  
	Test t(10);
	int i =t;   // 隐式类型转换可能导致bug
	cout <<"i = "<<i<<endl;
	return 0;
}

在这里插入图片描述
1.5.3 类类型之间的相互转换
类型转换函数 VS 转换构造函数
类型转换函数 和 转换构造函数 是互逆的

  • 失败举例
    // Value 中有转换构造函数
    //Test中有类型转换函数
    //编译器不知道调用哪个了
    //解决方式,在转换构造函数中加上 explicit关键字
class Test;
class Value
{
public:
	Value()
	{
				
	}
	Value(Test&t)
	{
	}
};
class Test
{
private:
	int mvalue;
public:
	Test(int value =0)
	{
		this->mvalue = value;
	}
	
	int value()
	{
		return mvalue;
	}
	operator Value()
	{
		Value ret;
		return ret;
	}
};

int main()
{  
	Test t(10);
	
	Value v =t;
	return 0;
}

在这里插入图片描述
1.5.4 结论

  • 类型转换函数可能与转换构造函数冲突
  • 无法抑制隐式的类型转换函数调用
  • 工程中以Type toType() 的公有成员代替类型转换函数
using namespace std;
class Test;
class Value
{
public:
	Value()
	{	
	}
	Value(Test&t)
	{
	}
};
class Test
{
private:
	int mvalue;
public:
	Test(int value =0)
	{
		this->mvalue = value;
	}
	int value()
	{
		return mvalue;
	}
	Value  toValue()   // 公有成员代替类型转换函数
	{
		Value ret;
		return ret;
	}
};
int main()
{  
	Test t(10);
	
	Value v =t.toValue();
	return 0;
}

1.5.5 QT中通过共有函数进行类型转换

int main()
{
    QString str = "";
    int i = 0;
    double d = 0;
    short s = 0;

    str = "-255";
    i = str.toInt();
    d = str.toDouble();
    s = str.toShort();

    qDebug() << "i = " << i << endl;
    qDebug() << "d = " << d << endl;
    qDebug() << "s = " << s << endl;

    return 0;
}

参考一 : 狄泰软件课程
如有侵权:请联系邮箱 1986005934@qq.com

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值