【问题标题】:refer to member variables by name or by index按名称或索引引用成员变量
【发布时间】:2017-12-17 17:34:05
【问题描述】:

我正在编写一个简单的类来处理我正在制作的一个小应用程序中的颜色。

此类将有 4 个双精度成员变量,并且希望能够通过名称(即:r、g、b 和 a)或索引(0 表示红色,1 表示绿色和等等)。

此外,如果能够使用大括号括起来的初始化列表进行初始化,那就太好了。

但是,我无法找到一种优雅而简洁的编码方式。

这是我最好的尝试,它并不漂亮。它也不处理无效索引。

struct Colour{
    double r, g, b, a;
    double& operator[](int i){
        switch(i){
        case 0:
            return r;
            break;
        case 1:
            return g;
            break;
        case 2:
            return b;
            break;
        case 3:
            return a;
            break;
        }
    }
    /* ... */
};
/* ... */
int main(){
    Colour c = {1.0, 2.0, 3.0, 4.0};
    std::cout << c.g << std::endl;
    c[1] = 10.0;
    std::cout << c.g << std::endl;
    return 0;
}

虽然这确实有效,但我想知道是否还有更多 (a) 优雅,并且 (b) 执行此操作的高性能(因为此应用程序主要处理像素操作)方式。

非常感谢。

【问题讨论】:

  • 优雅是主观的,没有明显的瓶颈。请先测量,再优化。

标签: c++ class reference


【解决方案1】:

您的代码在我看来很漂亮。它可以在 switch 语句中使用默认情况。我喜欢您专注于为班级用户创建灵活的工作流程,但总的来说,在 C++ 中没有免费的午餐。如果你想要更多的选择,你的课程会更复杂。

但是,既然你问了。如果优雅对你来说意味着保持你的类定义简洁......

// C++'11
struct Colour
{
    double v[4];

    double& r = v[0];
    double& g = v[1];
    double& b = v[2];
    double& a = v[3];

    double& operator[](int i)
    {
        assert(i >= 0 && i < 4);  // #include <assert.h>
        return v[i];
    }
};


void test()
{
    Colour c{ 1.0, 2.0, 3.0, 4.0 };
    std::cout << c.b << std::endl;
}

我遇到的问题:

Colour c{ 1.0, 2.0, 3.0, 4.0, 5.0 };  // does not compile (good!)

double x = 5.0;
Colour c{ 1.0, 2.0, 3.0, 4.0, x };  // compiles (...bad)

顺便说一句,断言在调试模式下会抛出一个有用的异常,但在发布模式下编译时会删除检查(通过标志)。

如果是我,我会使用你的代码并添加边界检查。

为了提高性能,此时您唯一需要注意的是不要使用低效的算法。稍后,如果你已经完成了分析并且这个类是一个瓶颈(LOL),我们可以尝试优化它。顺便说一句,代码简洁和优化不是朋友。

【讨论】:

  • 啊,谢谢!这正是我一直在寻找的,我不敢相信我没有想到这一点
  • 这是一种允许索引和按名称访问的聪明方法......我必须同意前面的评论:我不敢相信我没有想到这一点。对于构造函数,使用 std::array 不能解决这个问题吗?
  • 转念一想,这有一个大问题:引用占用了大量空间。所以你不能将它用于图像的像素,因为它会将每个像素放大参考的大小。如果这些只是“符号引用”就好了。
  • 我同意空间是个问题。 32 字节 -> 每个实例 48 字节(32 位程序)或 32 字节 -> 64 字节(64 位程序)。这一点很重要,不是因为空间问题,而是因为在这个级别上,物理接近会影响缓存带来的性能提升。但是,我没有提到这一点,因为我们现在不应该关心性能,gosh dangit!
【解决方案2】:

如果定义良好的整数(如项目)数量有限,您可以使用 C++11 之前的枚举器。您的枚举数可以是 RED=1、GREEN=2、BLUE=3 等。因此您的代码将更具可读性,但仍保持整数效率。

您需要转换为字符和/或字符串的唯一情况是您想要将数据输入或输出到文件中。在这种情况下,您只有一个转换功能。但据推测,输入/输出并不是使用这些值的主要部分,因此转换函数的性能不会受到关注。

【讨论】:

  • 我喜欢这个主意,但我不确定它是否适合我,不过谢谢
【解决方案3】:

这是我对此的看法,它基于@HumphreyWinnebago 解决方案,但试图解决尺寸增加的问题。这个想法是使用带有数组和未命名结构的联合。通过这种方式,您可以按名称访问结构元素,按索引访问数组元素。

#include <iostream>
#include <cassert>
#include <cstdint>
#include <array>

template<typename T>
union Colour {
    std::array<T,4> v;
    struct { T r,g,b,a; };

    T& operator[](std::size_t i) {
        assert(i < 4);
        return v[i];
    }
};

int main() {
    Colour<uint8_t> c{ 1, 2, 3, 4 };
    std::cout << int(c.b) << "\n";
    std::cout << int(c[1]) << "\n";
    std::cout << sizeof c << "\n";

    Colour<double> d{ 1.1, 2.2, 3.3, 4.4 };
    std::cout << d.b << "\n";
    std::cout << d[1] << "\n";
    std::cout << sizeof d << "\n";

    return 0;
}

不幸的是,我担心这严重依赖于特定于实现的结构对齐,但我相信这可能是一个有趣的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    相关资源
    最近更新 更多