为结构创建“拷贝构造”与“赋值重载”的辅助宏
2011-11-09 15:28
253 查看
使用STL的容器的时候,如果容器内的元素为自定义的struct,那么该struct必须具备拷贝构造函数和赋值操作符重载,虽然这两个函数实现起来非常简单,但如果结构内部数据元素比较多,还是要敲很多代码的。程序员一般是最讨厌重复写这么没有技术含量的代码的,一般来说,这样的结构定义都放在头文件里面。如果为这两个函数写了太多行代码,也会导致头文件太长。
下面是几个辅助宏,可以用来减少代码行数,并减少犯错的机会。
注:上面的辅助宏,只适应2~7个数据元素的struct。1个数据元素没必要单独实现一个struct,超过7个数据的结构……我想这样的结构作为容器元素是不是太浪费了。
下面是一个例子:
上面一个小小的例子,就精简了7行代码,如果这样的结构比较多,会省下不少代码行。虽然我一向不崇尚为减少代码行而增加程序逻辑复杂性,也很反对为减少代码行而忽视代码的规范性和可读性。但在软件开发过程中,在不增加程序逻辑复杂性的前提下,代码行越少,代码可读性越强。某种意义上来说,代码的可读性直接影响程序的健壮性。
此外,依靠设计的方法来保证代码的质量,比依靠程序员的细致与严谨来保证代码质量更可靠。
使用宏常常可以减少重复性编码时所犯下的错误。还是举个例子,上面那个简化之前的数据结构,有一个错误。
错误是这样的,在Node的拷贝构造函数中,我把b(node.b)误写成了b(node.a)。这样的错误,很隐蔽吧?如果在一个大的软件项目中,这个错误潜藏在系统中,会很折腾人的——数据无缘无故错乱,无任何规律,代码审查很难发现错误(谁会想到错误在这个拷贝构造这儿啊)。鄙人也曾经不幸中招,后来对系统抽丝剥茧,仔细跟踪,才终于发现问题。而且当初所有参与代码审查的人居然都没有能够发现这个错误。而这个错误是非常容易犯下的,编码速度越快的人,越容易犯下这种错误。我当时用的VC助手,输入node.之后,助手自动提示了a(因为最近一次的输入是node.a),一个回车下去,错误就发生了……
而使用宏,这样的悲剧不可能发生。一组简单的辅助宏,用了好多地方了,给自己留个纪念吧。
下面是几个辅助宏,可以用来减少代码行数,并减少犯错的机会。
//下面是为减少代码行的辅助宏,实现拷贝构造和赋值重载 #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;} |
下面是一个例子:
//简化之前的数据架构 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); }; |
此外,依靠设计的方法来保证代码的质量,比依靠程序员的细致与严谨来保证代码质量更可靠。
使用宏常常可以减少重复性编码时所犯下的错误。还是举个例子,上面那个简化之前的数据结构,有一个错误。
错误是这样的,在Node的拷贝构造函数中,我把b(node.b)误写成了b(node.a)。这样的错误,很隐蔽吧?如果在一个大的软件项目中,这个错误潜藏在系统中,会很折腾人的——数据无缘无故错乱,无任何规律,代码审查很难发现错误(谁会想到错误在这个拷贝构造这儿啊)。鄙人也曾经不幸中招,后来对系统抽丝剥茧,仔细跟踪,才终于发现问题。而且当初所有参与代码审查的人居然都没有能够发现这个错误。而这个错误是非常容易犯下的,编码速度越快的人,越容易犯下这种错误。我当时用的VC助手,输入node.之后,助手自动提示了a(因为最近一次的输入是node.a),一个回车下去,错误就发生了……
而使用宏,这样的悲剧不可能发生。一组简单的辅助宏,用了好多地方了,给自己留个纪念吧。
相关文章推荐
- 构造函数和析构函数,拷贝构造,赋值重载,调用练习
- 构造函数和析构函数,拷贝构造,赋值重载,调用练习
- 用char*实现的一个完整的类,包含类的基本操作:一般构造、拷贝构造、赋值转换、重载 > >,< <
- 用char*实现的一个完整的类,包含类的基本操作:一般构造、拷贝构造、赋值转换、重载 > >,< <
- 派生类的拷贝构造 与 赋值重载
- 拷贝构造和赋值重载,移动拷贝,赋值拷贝
- 第5章 构造、结构、拷贝、语意学
- 拷贝构造和赋值运算符函数的重载
- 经典笔题--重写字符串类 (构造,拷贝,析构,重载)
- [笔试题 1][c/c++]关于默认构造,拷贝构造,重载赋值运算符,隐式转化
- 结构体的构造、拷贝、赋值、析构和字符串的拷贝、构造、赋值和析构函数的比较
- A.3.2-创建简单的类(人类),包含的概念(字段,构造,封装字段,创建方法,创建对象,赋值,调用方法)
- 模板拷贝构造与赋值的不对称
- 拷贝构造与拷贝赋值
- string类构造、拷贝构造、赋值、操作符函数实现及注意事项
- 批注:C++中复制构造函数与重载赋值操作符总结:默认浅拷贝,带指针的需要深拷贝
- 复习几个C++概念:声明与定义、传值与拷贝构造、初始化和赋值
- 构造方法创建字符串对象与直接赋值方式创建对象
- 构造、拷贝(复制)构造、赋值构造以及析构
- 三大函数:拷贝构造,拷贝赋值,析构函数