C++构造函数一些常见的坑

 更新时间:2022年01月03日 15:31:17   作者:| 梁唐  
这篇文章主要给大家分享的是C++构造函数一些常见的坑,文章围绕C++构造函数的相关资料展开关于C++构造函数坑的内容,具有一定的参考价值,需要的小伙伴可以参考一下

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

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

我们很快写好了代码:

#include <iostream>
#ifndef STRINGBAD_H_
#define STRINGBAD_H_
class StringBad {
    private:
     char *str;
     int len;
     static int num_strings;
    public:
     StringBad(const char* s);
     StringBad();
     ~StringBad();
     friend std::ostream & operator << (std::ostream &os, const StringBad & st);
};
#endif

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

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

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

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

#include <cstring>
#include "stringbad.h"

using std::cout;

int StringBad::num_strings = 0;

StringBad::StringBad(const char* s) {
    len = std::strlen(s);
    str = new char[len+1];
    std::strcpy(str, s);
    num_strings++;
    cout << num_strings << ": \"" << str << "\" object created \n";
}

StringBad::StringBad() {
    len = 4;
    str = new char[4];
    std::strcpy(str, "C++");
    num_strings++;
    cout << num_strings << ": \"" << str << "\" object created \n";
}

StringBad::~StringBad() {
    cout << "\"" << str << "\" object deleted, ";
    --num_strings;
    cout << num_strings << " left \n";
    delete []str;
}

std::ostream & operator<<(std::ostream & os, const StringBad &st) {
    os << st.str;
    return os;
}

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

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

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

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

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

void callme(StringBad sb) {
 cout << "    \"" << sb << "\"\n";
}

然后我们这么使用:

int main() {
 StringBad sb("test");
 callme(sb);
 return 0;
}

会得到一个奇怪的结果:

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

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

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

void callme(StringBad &sb) {
 cout << "    \"" << sb << "\"\n";
}

int main() {
 StringBad sb("test");
 callme(sb);
 return 0;
}

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

int main() {
 StringBad sb("test");
 StringBad sports("Spinach Leaves Bowl for Dollars");
 StringBad sailor = sports;
 StringBad knot;
 StringBad st = sb;
 return 0;
}

执行一下,得到:

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

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

它等价于:

StringBad st = StringBad(sb);

对应的构造函数原型是:

StringBad(const StringBad&);

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

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

相关文章

  • 简单谈谈C++ 中指针与引用

    简单谈谈C++ 中指针与引用

    下面用通俗易懂的话来概述一下,指针-对于一个类型T,T*就是指向T的指针类型,也即一个T*类型的变量能够保存一个T对象的地址,而类型T是可以加一些限定词的,引用-引用是一个对象的别名,主要用于函数参数和返回值类型,符号X&表示X类型的引用。
    2015-09-09
  • wince禁止程序标题栏上的退出按钮示例

    wince禁止程序标题栏上的退出按钮示例

    这篇文章主要介绍了wince禁止程序标题栏上的退出按钮示例,需要的朋友可以参考下
    2014-02-02
  • 共用体的定义与应用详细解析

    共用体的定义与应用详细解析

    共同体的定义类似结构体,不过共同体的所有成员都在同一段内存中存放,起始地址一样,并且同一时刻只能使用其中的一个成员变量
    2013-08-08
  • C语言创建动态dll和调用dll(visual studio 2013环境下)

    C语言创建动态dll和调用dll(visual studio 2013环境下)

    本篇文章主要介绍了C语言创建动态dll和调用dll(visual studio 2013环境下),非常具有实用价值,需要的朋友可以参考下
    2017-11-11
  • C语言编程之扫雷小游戏空白展开算法优化

    C语言编程之扫雷小游戏空白展开算法优化

    扫雷是电脑上很经典的游戏,特意去网上玩了一会,几次调试之后,发现这个比三子棋要复杂一些,尤其是空白展开算法上和堵截玩家有的一拼,与实际游戏差别较大,不能使用光标,下面来详解每一步分析
    2021-09-09
  • C++二分查找算法实例

    C++二分查找算法实例

    这篇文章主要为大家详细介绍了C++二分查找算法的实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • 纯C语言:递归二进制转十进制源码分享

    纯C语言:递归二进制转十进制源码分享

    这篇文章主要介绍了纯C语言:递归二进制转十进制源码,有需要的朋友可以参考一下
    2014-01-01
  • C++常对象精讲_const关键字的用法

    C++常对象精讲_const关键字的用法

    用const修饰的声明数据成员称为常数据成员。变量或对象被 const修饰后其值不能被更新。因此被const修饰的变量或对象必须要进行初始化
    2013-10-10
  • C++实现关机功能详细代码

    C++实现关机功能详细代码

    大家好,本篇文章主要讲的是C++实现关机功能详细代码,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • C++读取单个字符操作示例详解

    C++读取单个字符操作示例详解

    这篇文章主要为大家介绍了C++读取单个字符操作示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09

最新评论