栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

没有学不会的C++: 为什么不要使用全局变量

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

没有学不会的C++: 为什么不要使用全局变量

在写程序时,我们都知道一条规范:不要使用全局变量。至于为什么,有可能是因为它会污染命名空间,也有可能是因为它会造成程序的不确定性,本文主要使用一个例子,来说明全局变量是如何让程序变得不确定的。

我们先定义两个类,一个 Cat,一个 Dog,如下是 cat.h 和 cat.cc 文件

// cat.h
#include 
using namespace std;

class Cat;
extern Cat c;

class Cat {
public:
    Cat(char* name);
    void meow();
private:
    char* _name;
};

// cat.cc
#include "cat.h"
#include "dog.h"

Cat c("mimi");
Cat::Cat(char* name) {
    cout << "construct cat" << endl;
    _name = name;
}

void Cat::meow() {
    cout << "cat " << _name << " meow" << endl;
}

Cat 类很简单,只有一个成员 _name,它是一个指针变量,且通过 meow 方法把它打印到屏幕上,同时,我们还定义了一个全局变量 Cat c("mimi");,同样的,Dog 类的定义也很简单,如下:

// dog.h
#include 
using namespace std;

class Dog;
extern Dog d;

class Dog {
public:
    Dog(char* name);
    void bark();
private:
    char* _name;
};

// dog.cc
#include "dog.h"
#include "cat.h"

Dog d("kobe");
Dog::Dog(char* name) {
    cout << "construct dog" << endl;
    _name = name;
}    
     
void Dog::bark() { 
    cout << "dog " << _name << " bark" << endl;
}

我们给 Dog 也定义了一个全局变量 d("kobe");,此时,我们修改一下 Cat 的构造函数,在里面引用全局变量 d,看看会发生什么

Cat::Cat(char* name) {
    cout << "construct cat" << endl;
    _name = name;
    d.bark();
}

编译运行前,别忘了我们的入口文件 main.cc,如下

int main() {
    return 0;
}

现在,我们将其进行编译运行

...
g++ -o main main.o cat.o dog.o -std=c++11  // 编译时的输出,说明了链接顺序
$ ./main
construct cat
// [1]    50759 segmentation fault  ./main 

可以看到程序崩溃了,崩溃原因是我们刚才在 Cat 构造函数中增加的一行调用全局变量的代码,因为在调用这行代码时,全局变量 d (实际上是 d._name )还没初始化。

而全局变量的初始化顺序是由编译器决定的,所以如果我们的全局变量间又有互相依赖的话,就很容易造成程序崩溃。

避免使用全局变量的方法也有很多,其中最广泛的应数 Singleton 模式了,针对上面的代码,我们可以定义一个 Singleton 类,其中包含 Cat 和 Dog 的静态指针,如下

// singleton.h
class Cat;
class Dog;
class Singleton {
    static Dog* pd;
    static Cat* pc;
public:
    ~Singleton();

    static Dog* getDog();
    static Cat* getCat();
};

与此同时,我们还声明了两个静态方法,用来获取 Dog 或 Cat 的指针,并且,我们希望 Dog 和 Cat 是以 lazy 的方式进行初始化的,即下面的 singleton.cc 文件的实现

// singleton.cc
#include "singleton.h"
#include "dog.h"
#include "cat.h"

Dog* Singleton::pd = 0;
Cat* Singleton::pc = 0;
Singleton::~Singleton() {
    delete pd;
    delete pc;
    pd = 0;
    pc = 0;
}
Dog* Singleton::getDog() { 
    if (pd == 0) {
 pd = new Dog("kobe");  
    }
    return pd;
}
Cat* Singleton::getCat() {
    if (pc == 0) {
 pc = new Cat("mimi");
    }
    return pc;
}

可以看到初始化 Cat 和 Dog 的时机是在第一次调用 getCat 和 getDog 时。现在你就可以删掉程序中的全局变量了,当你需要使用 Cat 对象或 Dog 对象时,直接调用 Singleton::getCat() 或 Singleton::getDog() 即可。

参考:

  • https://www.youtube.com/watch?v=hE77OSTE2J0&t=87s
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号