C++中替代static和global的方案有哪些?

11

我有一个类实例需要被其他类访问。

  • 每次通过构造函数传递这个实例会很繁琐。
  • 我试图避免全局变量,因为人们倾向于反对这种做法。
  • 我想将这个实例声明为一个类的静态成员,然后包含该类以便访问该实例,但这也不起作用。

错误:调用 'Foo' 类的私有构造函数

进一步说明问题,在QGraphicsView框架的上下文中: 我想将由控制器类(管理项目)实例化的QGraphicsItems添加到QGraphicsScene中,而QGraphicsScene是(但我不坚持这个细节)我的QMainWindow类的成员。

我在网上花了相当多的时间搜索,但我还是新手,在这里有点卡住了。欢迎任何鼓励和建议,告诉我解决这个困境的最佳方法。


5
类中的静态变量与全局变量存在一些相同的缺点。最为清晰简洁的方式是使用“繁琐”的参数传递。 - deviantfan
3
请查看单例模式:http://zh.wikipedia.org/wiki/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F - Caduchon
4
@Caduchon...除非因为同样的原因而不喜欢单例(毕竟,通常基于静态类变量的单例)。一些阅读资料:https://dev59.com/YXVC5IYBdhLWcg3w9GA9 等等。 - deviantfan
8
单例模式只不过是一个赞美过的全局变量。如果你发现在构造函数中传递参数很麻烦,那么可能是时候以某种方式简化你的依赖关系了。 - Benjy Kessler
是的,单例模式确实很有用。但人们往往过度使用它。在我听到/看到的大多数情况下,我认为没有使用它更好。 - deviantfan
显示剩余2条评论
2个回答

2
使用全局变量的做法一直备受争议,因此我想指出,以下内容是我的个人意见,欢迎讨论。
我不知道除了传递实例之外还有其他解决问题的方法,而且我个人认为,在某些情况下,全局实例并不是一件坏事。
  • 当我设计一个库时,我倾向于避免使用全局对象,除了一个工厂。全局对象通常被用作快捷方式,但程序员并不需要快捷方式,或者为了避免一些打字错误。此外,库中的大多数类应该是独立的。如果在这些类中使用全局实例,则会牺牲独立性。

  • 当我在一个类中使用静态变量时,我只会在整个类是单例或用于工厂方法(例如创建或获取)时使用它们,而不是像您描述的那样使用。按照您所描述的方式,对于其他开发人员来说是意外的,因为您需要第三方来初始化那个静态对象。这样做没有任何好处,除了避免输入更长的构造函数。这不能成为目标。

  • 如果我使用全局实例,我总是会将其包装在一个管理类中。我从不直接使用 QGraphicsItems 等第三方类作为全局变量(也不使用“原始”类或类型)。就像在第2点中所述,其他人可能不会将其视为全局变量。更重要的是,不清楚谁需要填充或销毁该实例。一个“GraphicsItemManager”可以有一个“setup”方法,这使得第三方用户完全清楚。

  • 在我的看法中,并不是在每种情况下都传递实例是最好的方法。并非所有的类都是为了重用而存在的,它们仅仅是为了一个项目而存在。在这种情况下,实现速度、易用性和清晰的分组比“无全局变量”的教条更重要。大多数时候,我编写管理类来分组例如图形项的实例。只要我遵循了前面提到的规则,我认为这并不会使代码变得难以阅读。

  • 有时资源在构建时不可用,因此您必须传递一个包装器。这导致您必须通过那些本身不需要该资源的类来传递实例,或者它们本身是库的一部分(在这种情况下,通常无法更改构造函数)。这可能会(因为我们都是人)导致误解。我使用全局管理类来跨越这些“空隙”,因为我认为保持该空间不受依赖关系的影响更为重要。

正如我所写的,这是我的个人意见。

0

当你有一个设计,其中类A创建类B,类B创建类C,类C需要在其构造函数中接受类D的实例时,就会出现这个问题。然后你必须通过ABC传递D。如果你将设计更改为具有创建实例函数(在主函数或某个工厂类中),该函数知道如何创建ABCD。那么你可以编写以下函数:

D d;
C c(d);
B b(c);
A a(b);

这种方式每个类只接受它的依赖项。

如果您后来意识到B还需要使用类D,那么您只需更改B的构造函数,而不会影响A的构造函数。


那么a->b->c->d->doSomething(a)是什么意思? 这和总是传递指向同一实例的指针有什么区别吗? D d; C c(d); B b(d); A a(d); - Sharky Bamboozle
1
谁在调用doSomething?调用它的类应该持有D而不是A。 - Benjy Kessler
假设A是要添加到QGraphicsScene D中的GraphicsItem,代码应该如下所示:a->d->addItem(a); 或者我误解了你的示例? - Sharky Bamboozle
1
这个想法并不是要有一个深层次的结构,而是让每个类只知道它自己的依赖关系,而不是它的子类的依赖关系。如果类a的一个函数想要调用类d的一个函数,那么它应该持有类d的一个实例。 - Benjy Kessler
如果你指的是子类,那么你是在谈论继承吗?因为控制器类只是实例化项目,没有调用超级构造函数。 - Sharky Bamboozle
1
不,我的意思是组合。子类==由组合所持有的实例。 - Benjy Kessler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接