经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
C++构造函数一些常见的坑
来源:jb51  时间:2022/1/3 16:47:22  对本文有异议

文章转自微信 公众号:Coder梁(ID:Coder_LT)

某一天我们接到了一个需求,需要开发一个类似于STLstring的类。

我们很快写好了代码:

  1. #include <iostream>
  2. #ifndef STRINGBAD_H_
  3. #define STRINGBAD_H_
  4. class StringBad {
  5. ? ? private:
  6. ? ? ?char *str;
  7. ? ? ?int len;
  8. ? ? ?static int num_strings;
  9. ? ? public:
  10. ? ? ?StringBad(const char* s);
  11. ? ? ?StringBad();
  12. ? ? ?~StringBad();
  13. ? ? ?friend std::ostream & operator << (std::ostream &os, const StringBad & st);
  14. };
  15. #endif

在这个.h文件当中,我们定义了一个StringBad类,这是C++ Primer当中的一个例子。为什么叫StringBad呢,主要是为了提示,表示这是一个没有完全开发好的demo

这里有一个小细节,我们在类当中定义的是一个char *也就是字符型指针,而非字符型数组。这意味着我们在类声明当中没有为字符串本身分配空间,而是在构造函数当中使用new来完成的,避免了预先定义字符串的长度。

其次num_strings是一个静态成员,也就是说无论创建了多少对象,它都只会保存一份。类的所有成员共享同一个静态变量。

接下来我们来看一下它的实现:

  1. #include <cstring>
  2. #include "stringbad.h"
  3.  
  4. using std::cout;
  5.  
  6. int StringBad::num_strings = 0;
  7.  
  8. StringBad::StringBad(const char* s) {
  9. ? ? len = std::strlen(s);
  10. ? ? str = new char[len+1];
  11. ? ? std::strcpy(str, s);
  12. ? ? num_strings++;
  13. ? ? cout << num_strings << ": \"" << str << "\" object created \n";
  14. }
  15.  
  16. StringBad::StringBad() {
  17. ? ? len = 4;
  18. ? ? str = new char[4];
  19. ? ? std::strcpy(str, "C++");
  20. ? ? num_strings++;
  21. ? ? cout << num_strings << ": \"" << str << "\" object created \n";
  22. }
  23.  
  24. StringBad::~StringBad() {
  25. ? ? cout << "\"" << str << "\" object deleted, ";
  26. ? ? --num_strings;
  27. ? ? cout << num_strings << " left \n";
  28. ? ? delete []str;
  29. }
  30.  
  31. std::ostream & operator<<(std::ostream & os, const StringBad &st) {
  32. ? ? os << st.str;
  33. ? ? return os;
  34. }

首先,我们可以注意到第一句就是将num_strings初始化成了0,我们不能在类声明中初始化静态成员变量。因为声明只是描述了如何分配内存,但并不真的分配内存。

所以对于静态类成员,我们可以在类声明之外使用单独的语句进行初始化。因为静态成员变量是单独存储的,并不是对象的一部分。

初始化要在方法文件也就是cpp文件当中,而不是头文件中。因为头文件可能会被引入多次,如果在头文件中初始化将会引起错误。当然也有一种例外,就是加上了const关键字。

从逻辑上看,我们这样实现并没有任何问题,但是当我们执行的时候,就会发现问题很多……

假设我们现在有一个函数:

  1. void callme(StringBad sb) {
  2. ?cout << " ? ?\"" << sb << "\"\n";
  3. }

然后我们这么使用:

  1. int main() {
  2. ?StringBad sb("test");
  3. ?callme(sb);
  4. ?return 0;
  5. }

会得到一个奇怪的结果:

从屏幕可以看到我们的析构函数执行了两次,一次很好理解应该是main函数退出的时候自动执行的,还有一次呢?是什么时候执行的?

答案是执行callme函数的时候执行的,因为callme函数使用了值传递。当callme函数执行结束时,也会调用参数sb的析构函数。

如果我们改成引用传递,就一切正常了:

  1. void callme(StringBad &sb) {
  2. ?cout << " ? ?\"" << sb << "\"\n";
  3. }
  4.  
  5. int main() {
  6. ?StringBad sb("test");
  7. ?callme(sb);
  8. ?return 0;
  9. }

这还没完,我们把代码再改一下,会发现还有问题:

  1. int main() {
  2. ?StringBad sb("test");
  3. ?StringBad sports("Spinach Leaves Bowl for Dollars");
  4. ?StringBad sailor = sports;
  5. ?StringBad knot;
  6. ?StringBad st = sb;
  7. ?return 0;
  8. }

执行一下,得到:

会发现又有负数出现了,这是为什么呢?

因为我们执行了StringBad st = sb这样的操作,这个操作并不会调用我们实现的任何一个构造函数。

它等价于:

  1. StringBad st = StringBad(sb);

对应的构造函数原型是:

  1. StringBad(const StringBad&);

当我们用一个对象来初始化另外一个对象的时候,编译器将会自动生成上述的构造函数。这样的构造函数叫做拷贝构造函数,由于我们没有重载拷贝构造函数,因此它不知道要对num_strings变量做处理,也就导致了不一致的发生。

到此这篇关于C++构造函数一些常见的坑的文章就介绍到这了,更多相关C++构造函数的坑内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号