一片云雾

写博客挺浪费时间的……
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

为结构创建“拷贝构造”与“赋值重载”的辅助宏

Posted on 2011-11-09 15:28  一片云雾  阅读(1132)  评论(0编辑  收藏  举报

使用STL的容器的时候,如果容器内的元素为自定义的struct,那么该struct必须具备拷贝构造函数赋值操作符重载,虽然这两个函数实现起来非常简单,但如果结构内部数据元素比较多,还是要敲很多代码的。程序员一般是最讨厌重复写这么没有技术含量的代码的,一般来说,这样的结构定义都放在头文件里面。如果为这两个函数写了太多行代码,也会导致头文件太长。

下面是几个辅助宏,可以用来减少代码行数,并减少犯错的机会

//下面是为减少代码行的辅助宏,实现拷贝构造和赋值重载
#define M_COPY_C2(T, a, b)                T(const T &t) : a(t.a), b(t.b) {}
#define M_COPY_C3
(T, a, b, c)            T(const T &t) : a(t.a), b(t.b), c(t.c) {}

#define M_COPY_C4
(T, a, b, c, d)        T(const T &t) : a(t.a), b(t.b), c(t.c), d(t.d) {}

#define M_COPY_C5
(T, a, b, c, d, e)        T(const T &t) : a(t.a), b(t.b), c(t.c), d(t.d), e(t.e) {}

#define M_COPY_C6
(T, a, b, c, d, e, f)        T(const T &t) : a(t.a), b(t.b), c(t.c), d(t.d), e(t.e), f(t.f){}

#define M_COPY_C7
(T, a, b, c, d, e, f, g)    T(const T &t) : a(t.a), b(t.b), c(t.c), d(t.d), e(t.e), f(t.f), g(t.g){}

#define M_ASSIGN2
(T, a, b)                T& operator = (const T &t){a=t.a; b=t.b; return *this;}

#define M_ASSIGN3
(T, a, b, c)            T& operator = (const T &t){a=t.a; b=t.b; c=t.c; return *this;}

#define M_ASSIGN4
(T, a, b, c, d)        T& operator = (const T &t){a=t.a; b=t.b; c=t.c; d=t.d; return *this;}

#define
M_ASSIGN5(T, a, b, c, d, e)        T& operator = (const T &t){a=t.a; b=t.b; c=t.c; d=t.d; e=t.e; return *this;}

#define M_ASSIGN6
(T, a, b, c, d, e, f)        T& operator = (const T &t){a=t.a; b=t.b; c=t.c; d=t.d; e=t.e; f=t.f; return *this;}

#define M_ASSIGN7
(T, a, b, c, d, e, f, g)    T& operator = (const T &t){a=t.a; b=t.b; c=t.c; d=t.d; e=t.e; f=t.f; g=t.g; return *this;}

注:上面的辅助宏,只适应2~7个数据元素的struct。1个数据元素没必要单独实现一个struct,超过7个数据的结构……我想这样的结构作为容器元素是不是太浪费了。

下面是一个例子:

//简化之前的数据架构
struct Node
{
    int a;

    int b;

    string c;

    Node() : a(0), b(0) {}
    Node(const Node &node) : a(node.a), b(node.a), c(node.c)
    {}
    Node & operator = (const Node &node)
    {
        a = node.a;
        b = node.b;
        c = node.c;
        return *this;
    }
};

//使用宏简化之后的数据结构
struct Node
{
    int a;

    int b;

    string c;

    Node() : a(0), b(0) {}
    M_COPY_C3(a, b, c);
    M_ASSIGN3(a, b, c);
};

上面一个小小的例子,就精简了7行代码,如果这样的结构比较多,会省下不少代码行。虽然我一向不崇尚为减少代码行而增加程序逻辑复杂性,也很反对为减少代码行而忽视代码的规范性和可读性。但在软件开发过程中,在不增加程序逻辑复杂性的前提下,代码行越少,代码可读性越强。某种意义上来说,代码的可读性直接影响程序的健壮性。

此外,依靠设计的方法来保证代码的质量,比依靠程序员的细致与严谨来保证代码质量更可靠。

使用宏常常可以减少重复性编码时所犯下的错误。还是举个例子,上面那个简化之前的数据结构,有一个错误。

错误是这样的,在Node的拷贝构造函数中,我把b(node.b)误写成了b(node.a)。这样的错误,很隐蔽吧?如果在一个大的软件项目中,这个错误潜藏在系统中,会很折腾人的——数据无缘无故错乱,无任何规律,代码审查很难发现错误(谁会想到错误在这个拷贝构造这儿啊)。鄙人也曾经不幸中招,后来对系统抽丝剥茧,仔细跟踪,才终于发现问题。而且当初所有参与代码审查的人居然都没有能够发现这个错误。而这个错误是非常容易犯下的,编码速度越快的人,越容易犯下这种错误。我当时用的VC助手,输入node.之后,助手自动提示了a(因为最近一次的输入是node.a),一个回车下去,错误就发生了……

而使用宏,这样的悲剧不可能发生。一组简单的辅助宏,用了好多地方了,给自己留个纪念吧。