转换函数(C++)的意义是什么

What is the significance of conversion function (C++)

本文关键字:是什么 函数 C++ 转换      更新时间:2023-10-16

我发现了转换函数的概念。这是同时使用getter和转换函数的示例代码。

#include<iostream>
using namespace std;
class conv
{
  int val;
  int a,b;
  public:
  conv(int x, int y)
  {
    a = x;
    b = y;
    val = 1;
    val = a*b;
  }
  int get(){ return val; };
  operator int(){ return val; };
};
int main()
{
  conv obj(1,2);
  int x;
  x = obj; // using conversion function
  cout<<"Using conversion function"<<x;
  cout<<"Using getters"<<obj.get();
  return 0;
 }

两个cout语句产生相同的输出。我想知道转换函数相对于getter函数的特殊意义是什么,因为getter函数可以实现同样的意义?

谢谢。

转换运算符(不带explicit关键字)的主要目的是提供类型到目标类型的隐式转换。这意味着(例如)如果您有一个采用目标类型的函数(在您的示例中,是int):

void function (int value) {...}

然后,您可以传递类型的实例,编译器将隐式调用转换运算符,以便调用函数:

// your code:
conv obj(1,2);
function(obj); // compiler will call conv::operator int()

在我参与过的大多数项目中,使用这些都是不受欢迎的,因为使用C++的这一功能会使代码更难理解。

explicit关键字通知编译器不允许隐式转换(在C++11之前,这仅适用于转换构造函数)。在这种情况下,您必须显式地强制转换对象:

// your code:
conv obj(1,2);
function(static_cast<int>(obj)); // compiler will call conv::operator int()

getter/setter和转换运算符有不同的用途:

  1. 访问器用于使对私人成员的访问成为公共的。通过只定义一个,您可以实现公共写入或读取访问,而无需将另一个公开。

  2. 转换运算符用于将整个对象转换为不同的类型。

IMHO,你不应该经常这样做:访问器是一种代码气味,类只不过是一个数据结构,这不是OOP(尽管大多数人没有意识到他们实际上在定义一个公共成员)。

转换运算符的问题是,它们使代码不太容易理解,因为转换在调用代码中不可见。而且,与转换构造函数(只接受一个参数的构造函数)结合使用,它们很容易导致大量"转换是模糊的"编译器错误。

通常,您想要编写的唯一转换运算符是到bool的转换,回答实例是否从根本上可用的问题。这样你就可以写这样的代码:

if(myInstance) {
    //do something sensible
} else {
    //handle error
}

在这种特殊情况下,它非常无用。

我最近写了一些处理像素的代码,它使用转换函数从每个R、G、B、a值的8位中生成16位像素。

class Pixel
{
    uint8_t a, r, g, b;
public:
    Pixel() : a(0xff), r(0), g(0), b(0) {};
    Pixel(uint8_t aA, uint8_t aR, uint8_t aG, uint8_t aB) : a(aA), r(aR), g(aG), b(aB) {}
    Pixel(uint16_t rgb16) 
        {
            a = 0xff;
            r = ((rgb16 >> 11) & 31) << 3;
            g = ((rgb16 >> 5)  & 63) << 2;
            b = (rgb16 & 31) << 3;
        }
    Pixel(uint32_t argb32)
        {
            a = argb32 & 0xff;
            r = (argb32 >> 8) & 0xff;
            g = (argb32 >> 16) & 0xff;
            b = (argb32 >> 24) & 0xff;
        }
    uint8_t A() const { return a; }
    uint8_t R() const { return r; }
    uint8_t G() const { return g; }
    uint8_t B() const { return b; }
    void A(uint8_t aA) { a = aA; }
    void R(uint8_t aR) { r = aR; }
    void G(uint8_t aG) { g = aG; }
    void B(uint8_t aB) { b = aB; }
    operator uint16_t() 
        { 
            return ((r >> 3) << 11) |
                ((g >> 2) << 6) |
                (b >> 3); 
        }
};

它允许使用该代码的代码像这样工作:

uint16_t *frame_buffer;
...
for(...)
{
     ... 
     Pixel blended = Blend(above, below);
     frame_buffer[y * ScreenWidth + x] = blended;
}

首先,转换运算符提供了一个不那么冗长的sintax。我是唯一一个讨厌Java get/set语法的人
Bt此功能的主要目的是提供您自己的类型和指定类型之间的转换。这在以下情况下非常有用:

  • 对基本数字类型的转换是自然而有用的。例如:

    class fixed_point
    {
    private:
        long long int _bits;
    public:
        fixed_point(float number); //Cast from floating-point
        operator float(); //Cast to floating-point
    };
    

    请注意,在令人困惑和非自然的情况下,此功能很容易被过度使用。例如:

    struct complex_number
    {
        float real , imaginary;
        operator float(); //WTF!!! What is this? Real part? Imaginary part?
    };
    

    事实上,任何人都可能认为,使用命名构造函数习惯用法和getter:,"有用"的情况可能更可读

    class fixed_point
    {
    private:
        long long int _bits;
        fixed_point(long long int bits) _bits( bits ) {}
    public:
        static fixed_point from_floating_point(float number);
               float       to_floating_point();
    };
    
  • 对象状态标志:提供到布尔值的转换,以检查对象的状态(OK与ERROR)。例如,标准库流就是这样做的:

    while( cin >> input )
    {
        ...
    }
    

    这之所以有效,是因为流以一种实现流畅接口的方式实现operator>>,也就是说,允许级联操作,如:

    cout << "hello" << "world";
    

    流运算符的实现返回对传递给运算符的同一流的引用,即:

    istream& operator>>(istream& is , INPUT_TYPE input)
    {
        /* read ops */
        return is;
    }
    

    因此,当执行while(std::cin >> input)时,语句cin >> input是对operator>>重载的调用,该重载返回对std::cin的引用,并且该对象(cin)被隐式转换为布尔值,以在读取操作后检查cin的状态。

C++11提供了显式转换运算符,它不允许潜在的混淆隐式转换。只有当程序员以明确的方式指定转换时,才允许进行转换。

最后,C++常见问题解答有一篇文章,里面有一个很棒的描述、规则和在C++中重载运算符时要遵循的技术,包括转换运算符。

它不那么冗长。可能会让更加困惑。