C++ 类最佳实践

Posted

技术标签:

【中文标题】C++ 类最佳实践【英文标题】:C++ class best practice 【发布时间】:2009-03-09 04:34:34 【问题描述】:

我想知道设计 c++ 类时的一些最佳实践。

为了把它放在上下文中,我有一个名为 Vec3 的 c++ 类。

class Vec3
private:
    float elements[3];
public:
    Vec3(Vec3 v1)...
    Vec3(int x, int y, int z)...
    Vec3 add(Vec3 v1)...
    Vec3 add(int x, int y, int z)...
    ...
    Vec3 multiply(Vec3 v1)...
    ...
    int dotProduct(Vec3 v1)...
    Vec3 normalize()...
    ....
    int operator[](int pos)...
;

所以,我有这个类对大小为 3 的向量进行计算。 我想知道什么更好。是否使用指针。

我是否应该返回指针并将我的参数作为指针?

Vec3 add(Vec3 v1) or Vec3* add(Vec3 v1) or Vec3* add(Vec3* v1) or ....

现在我很困惑,我不知道我是否应该在课堂上使用指针。我想总有一种方法可以将我的参数发送给不处理指针的函数......

Vec3* v2 = new Vec3(1,1,1);
Vec3 sum = v1.add(*v2);

而且有一个解决方案可能是我能想到的最好的解决方案.. 兼具功能

Vec3 add(Vec3 v2)...
Vec3* add(Vec3* v2)...

但我担心这会导致重复代码并且可能会产生开销。

感谢您的回答...顺便说一句,我可以使用模板来更改 Vector 的大小,但我更喜欢单独保留 Vec3 类并创建一个 Vec4 类或将其命名为 Quaternion。

编辑 这是我带来的解决方案。随意评论或修改或重用代码。 一件事。我只想提一下,就我而言,此类应该是透明的。就像我们添加数字一样。

int i = 10;
int j = 15;
int k = i + k;

如果添加重载修改了在这种情况下调用函数的对象 i。 我最终会得到一个 k 是对 i 的引用并且 i 等于 25。但是我们在这里真正想要的是 k 等于 25 并且 i,k 不变。

这就是我的班级的工作方式。 Vec3 k = i + k 不会修改 i 或 k,因为我们正在从这些值创建一个新数字。我返回引用的唯一情况是 +=、-=、++、--...、set([XYZ])?并归一化。

做 myvec.setX(10).normalize().scale(10) 之类的事情可能会很有趣

注意:scale 应该返回一个引用。我没有看到它,但我想这样应该会更好。

Vec3 t = myvec.normalize().scale(100).copy();

http://pastebin.com/f413b7ffb

谢谢大家,我现在将学习 Matrix 课程。

【问题讨论】:

【参考方案1】:

这些是我通常坚持的规则。请注意“通常”,有时会有不同的做法......

对于我不打算修改的参数,如果它们不是太大,我会按值传递,因为它们会被复制。如果它们有点大或不可复制,您可以使用 const 引用或指针(我更喜欢 const 引用)。

对于我确实打算修改的参数,我使用参考。

对于返回值,我将尽可能返回一个副本。有时返回引用会很方便(这适用于获取/设置的单个函数,在获取或设置项目时不需要进行任何特殊处理)。

在我看来,指针真正闪耀的地方是例如我想要控制何时构造或销毁它的变量。

希望对您有所帮助。

【讨论】:

这正是我所需要的。谢谢你。我想我应该更多地使用引用。:)【参考方案2】:

由于 int 是原语,所以保持原样。对于任何有 vec3 的使用参考。

例如。

Vec3 add(const Vec3 &v1)...

在 C 中您会使用指针,但在 C++ 中,引用通常更适合对象。

【讨论】:

【参考方案3】:

向量具有已知的语义(为您和您的类的用户所知),因此我会考虑重载运算符(+、-、+=、-=)这样做,我会使用常规定义而不是更改他们:

// instead of add:
class Vec3 
public:
   Vec3& operator+=( Vec3 const & rhs );
;
// implemented as free function:
Vec3 operator+( Vec3 const &lhs, Vec3 const & rhs);

我会避免使用指针。引用更自然,只有极少数情况下您确实需要它们而不是引用/值。避免重复您的函数(带/不带指针),因为这会使您的代码不必要地更加复杂,正如您已经在问题中发布的那样,您始终可以取消引用指针以检索引用。

同时提供常量和非常量运算符[]:

class Vec3 
public:
   float operator[]( size_t pos ) const; // returns copy, data does not change
   float& operator[]( size_t pos );  // returns a reference and allows changing the contents
;

编辑:我忘了提及 size_t 细节:更喜欢使用 unsigned / size_t 作为索引参数而不是有符号整数。

【讨论】:

这确实是大多数时候要走的路。顺便说一句,const 下标运算符可以返回一个 const 引用(这可能会提供一些有趣的副作用);有些人更喜欢按值返回“const T”而不仅仅是“T”,请参阅 GOTW 关于这个主题。 “大多数时候”:有时用 x 运算符来实现 x= 运算符更容易。矩阵 * 和 *= 运算符就是这种情况。 这很有意义。有一件事我不确定。如果我有一个 const 和非 const 函数。编译器如何知道使用哪一个?但现在,我将保留其他函数而不仅仅是运算符,因为我需要它们来减少从 C 到 C++ 的重构。 当有 const 和 non-const 重载时,编译器会一直选择 non-const 函数,除非对象在 time/point/scope 是 const (对不起,我不能找到正确的词)该函数被调用。见parashift.com/c++-faq-lite/const-correctness.html#faq-18.12 @Luc Hermite:我不知道矩阵的特殊情况,但更常见的是用 operator*=: X& operator+( X const & a, X const & ) 定义 operator+ X tmp = a; tmp += b;返回 tmp; 【参考方案4】:

如果您实现operator+=()operator*=() 之类的运算符,您会希望它以Vec3& 的形式返回*this

Vec3& operator+=(const Vec3& v2) 
    // add op
    return *this;

对于其他基本运算符,例如 operator+() 和您的 add(),您需要返回一个副本

Vec3 operator+(const Vec3& v2) 
    Vec3 ret;
    // add
    return ret;

【讨论】:

【参考方案5】:

在这种情况下,您几乎肯定不希望参数成为指针。考虑以下示例以了解原因:

// Error: not possible to take the address of the temporary
//        return value. 
v2.add(&someFunctionReturningVec3());

对于常量的引用是没有问题的。您甚至可以轻松嵌套操作:

// declaration: Vec3 add(Vec3 const& v);
v2.add(v1.add(v3));

【讨论】:

【参考方案6】:

在这种情况下,不需要将参数作为指针,而且你真的不应该为所有这样的运算符返回一个新对象。 在 OO 编程中,想法是对实际对象进行操作,例如有

无效添加(Vec3 v1); 无效乘法(Vec3 v1);

我还要说,您应该坚持将 Vec3 对象作为参数(而不是 x、y、z)。如果只有x,y,z,可以调用add(Vec3(x,y,z))。

【讨论】:

返回 *this 允许方法链接:v.add(Vec(1,2,3)).multiply(4);。当然,使用运算符重载会更好一些,但优先级除外:(v += Vec(1,2,3)) *= 4;。但是 op+ 和朋友应该有复制语义,这会降低效率。 add 函数不应该修改对象。所以我需要退回一份。这就像写 j = i + k;其中 i 和 k 的值为 10,而最终 i 的值为 20。这没有任何意义。 @Sybiam 我不同意。运算符 add() 确实应该修改对象。但是,如果您重载运算符,则应该实现复制语义(正如 Simon 指出的那样)。如果你想在没有 op+ 的情况下实现“j = i + k”,你应该写 Vec3 j(i); j.add(k);【参考方案7】:

此页面包含有关此主题的非常好的讨论:http://www.cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html

【讨论】:

这将是一个很好的讨论,如果它不提倡自分配反模式。将二元运算符定义为成员函数也不是最先进的。【参考方案8】:

正如 grayfade 提到的,您应该关注复制语义。在这种情况下,您还应该添加这些方法:

class Vec3 
public:
  Vec3(const Vec3& rhs) 
    copy(rhs);
  

  Vec3 operator=(const Vec3& rhs) 
    copy(rhs);
    return *this;
  

private:
  void copy(const Vec3& rhs) 
    // copy state from rhs
  
;

【讨论】:

op= 应该返回 Vec3&,而不是 Vec3 您不需要为使用 std 容器或 pod 值的类编写自己的复制函数 - 让编译器为您完成。

以上是关于C++ 类最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

C++类设计和实现的十大最佳实践

C++ 类设计和实现的十大最佳实践

将类数据存储在随机访问文件中的最佳实践

C++最佳实践 | 1. 工具

C++最佳实践 | 6. 性能

C++最佳实践 | 1. 工具