300

如何在 C++ 中创建静态类?我应该能够做类似的事情:

cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;

假设我创建了这个BitParser类。BitParser类定义会是什么样子?

4

15 回答 15

316

如果您正在寻找一种将“静态”关键字应用于类的方法,例如您可以在 C# 中使用,那么您将无法不使用托管 C++。

但是您的示例看起来,您只需要在您的 BitParser 对象上创建一个公共静态方法。像这样:

位解析器.h

class BitParser
{
 public:
  static bool getBitAt(int buffer, int bitIndex);

  // ...lots of great stuff

 private:
  // Disallow creating an instance of this object
  BitParser() {}
};

位解析器.cpp

bool BitParser::getBitAt(int buffer, int bitIndex)
{
  bool isBitSet = false;
  // .. determine if bit is set
  return isBitSet;
}

您可以使用此代码以与示例代码相同的方式调用该方法。

于 2008-08-12T23:43:37.773 回答
271

考虑马特普莱斯的解决方案

  1. 在 C++ 中,“静态类”没有任何意义。最接近的是只有静态方法和成员的类。
  2. 使用静态方法只会限制你。

你想要的是,用 C++ 语义表达,把你的函数(因为它一个函数)放在一个命名空间中。

编辑 2011-11-11

C++ 中没有“静态类”。最接近的概念是只有静态方法的类。例如:

// header
class MyClass
{
   public :
      static void myMethod() ;
} ;

// source
void MyClass::myMethod()
{
   // etc.
}

但是您必须记住,“静态类”是类 Java 语言(例如 C#)中的 hack,它们不能具有非成员函数,因此它们必须将它们作为静态方法移动到类中。

在 C++ 中,您真正想要的是在命名空间中声明的非成员函数:

// header
namespace MyNamespace
{
   void myMethod() ;
}

// source
namespace MyNamespace
{
   void myMethod()
   {
      // etc.
   }
}

这是为什么?

在 C++ 中,命名空间比“Java 静态方法”模式的类更强大,因为:

  • 静态方法可以访问类私有符号
  • 私有静态方法对每个人仍然可见(如果无法访问),这在一定程度上违反了封装
  • 静态方法不能前向声明
  • 类用户不能在不修改库头的情况下重载静态方法
  • 没有什么比同一个命名空间中的(可能是朋友)非成员函数更好的静态方法可以完成的了
  • 命名空间有自己的语义(可以组合,可以匿名等)
  • 等等

结论:不要在 C++ 中复制/粘贴 Java/C# 的模式。在 Java/C# 中,该模式是强制性的。但在 C++ 中,这是不好的风格。

编辑 2010-06-10

有一个支持静态方法的论点,因为有时需要使用静态私有成员变量。

我有点不同意,如下所示:

“静态私有成员”解决方案

// HPP

class Foo
{
   public :
      void barA() ;
   private :
      void barB() ;
      static std::string myGlobal ;
} ;

首先,myGlobal 之所以称为 myGlobal,是因为它仍然是一个全局私有变量。看一下 CPP 的源代码将阐明:

// CPP
std::string Foo::myGlobal ; // You MUST declare it in a CPP

void Foo::barA()
{
   // I can access Foo::myGlobal
}

void Foo::barB()
{
   // I can access Foo::myGlobal, too
}

void barC()
{
   // I CAN'T access Foo::myGlobal !!!
}

乍一看,从封装的角度来看,免费函数 barC 无法访问 Foo::myGlobal 似乎是一件好事……这很酷,因为查看 HPP 的人将无法(除非诉诸破坏)访问Foo::myGlobal。

但是如果你仔细观察,你会发现这是一个巨大的错误:不仅你的私有变量仍然必须在 HPP 中声明(因此,尽管是私有的,但对全世界都是可见的),而且你必须声明在同一个 HPP 中,所有(如在 ALL 中)功能将被授权访问它!!!!

所以使用私人静态成员就像是裸体走在外面,皮肤上纹着你的爱人名单:没有人被授权触摸,但每个人都可以偷看。还有好处:每个人都可以拥有那些被授权与你的私人一起玩的人的名字。

private确实... :-D

“匿名命名空间”解决方案

匿名命名空间将具有使事物真正私有化的优势。

一、HPP头

// HPP

namespace Foo
{
   void barA() ;
}

只是为了确保您说:没有 barB 和 myGlobal 的无用声明。这意味着没有人阅读标题知道 barA 后面隐藏了什么。

然后,CPP:

// CPP
namespace Foo
{
   namespace
   {
      std::string myGlobal ;

      void Foo::barB()
      {
         // I can access Foo::myGlobal
      }
   }

   void barA()
   {
      // I can access myGlobal, too
   }
}

void barC()
{
   // I STILL CAN'T access myGlobal !!!
}

如您所见,就像所谓的“静态类”声明一样,fooA 和 fooB 仍然能够访问 myGlobal。但没有其他人可以。除了这个 CPP 之外,没有其他人知道 fooB 和 myGlobal 甚至存在!

与在她的皮肤上纹身的地址簿裸体行走的“静态类”不同,“匿名”命名空间是全包的,这似乎更好地封装了 AFAIK。

真的有关系吗?

除非您的代码的用户是破坏者(作为练习,我会让您找到如何使用肮脏的行为未定义的黑客访问公共类的私有部分......),即使它private是在标头中声明的类的部分中private可见。private

尽管如此,如果您需要添加另一个可以访问私有成员的“私有函数”,您仍然必须通过修改标头向全世界声明它,就我而言,这是一个悖论:如果我更改我的代码(CPP 部分),那么接口(HPP 部分)不应该改变。引用列奥尼达的话:“这是封装!

编辑 2014-09-20

什么时候类的静态方法实际上比具有非成员函数的命名空间更好?

当您需要将函数组合在一起并将该组提供给模板时:

namespace alpha
{
   void foo() ;
   void bar() ;
}

struct Beta
{
   static void foo() ;
   static void bar() ;
};

template <typename T>
struct Gamma
{
   void foobar()
   {
      T::foo() ;
      T::bar() ;
   }
};

Gamma<alpha> ga ; // compilation error
Gamma<Beta> gb ;  // ok
gb.foobar() ;     // ok !!!

因为,如果一个类可以是模板参数,那么命名空间就不能。

于 2008-09-21T22:55:43.297 回答
67

您还可以在命名空间中创建自由函数:

在 BitParser.h 中

namespace BitParser
{
    bool getBitAt(int buffer, int bitIndex);
}

在 BitParser.cpp 中

namespace BitParser
{
    bool getBitAt(int buffer, int bitIndex)
    {
        //get the bit :)
    }
}

一般来说,这将是编写代码的首选方式。当不需要对象时,不要使用类。

于 2008-08-13T00:26:43.740 回答
13

如果您正在寻找一种将“静态”关键字应用于类的方法,例如在 C# 中

静态类只是手握你并阻止你编写任何实例方法/变量的编译器。

如果您只是编写一个没有任何实例方法/变量的普通类,那是一样的,这就是您在 C++ 中所做的

于 2008-08-13T00:06:09.333 回答
12

我可以写类似的东西static class吗?

,根据C++11 N3337 标准草案附件 C 7.1.1:

更改:在 C++ 中,static 或 extern 说明符只能应用于对象或函数的名称。在 C++ 中将这些说明符与类型声明一起使用是非法的。在 C 中,这些说明符在用于类型声明时会被忽略。例子:

static struct S {    // valid C, invalid in C++
  int i;
};

基本原理:存储类说明符在与类型关联时没有任何意义。在 C++ 中,可以使用静态存储类说明符声明类成员。在类型声明中允许存储类说明符可能会使代码让用户感到困惑。

和 like 一样structclass也是一种类型声明。

通过遍历附录 A 中的语法树也可以推断出相同的结果。

有趣的是,这static struct在 C 中是合法的,但没有效果:为什么以及何时在 C 编程中使用静态结构?

于 2015-07-03T08:22:19.150 回答
11

在 C++ 中,您想创建一个类的静态函数(不是静态类)。

class BitParser {
public:
  ...
  static ... getBitAt(...) {
  }
};

然后,您应该能够使用 BitParser::getBitAt() 调用该函数,而无需实例化我认为是所需结果的对象。

于 2008-08-12T23:43:34.840 回答
8

正如这里所指出的,在 C++ 中实现这一点的更好方法可能是使用命名空间。但是由于没有人在final这里提到这个关键字,我发布了一个直接等效static class于 C# 在 C++11 或更高版本中的样子:

class BitParser final
{
public:
  BitParser() = delete;

  static bool GetBitAt(int buffer, int pos);
};

bool BitParser::GetBitAt(int buffer, int pos)
{
  // your code
}
于 2018-03-21T11:52:10.910 回答
5

如前所述,您“可以”在 C++ 中拥有一个静态类,静态类是没有任何对象实例化它的类。在 C++ 中,这可以通过将构造函数/析构函数声明为私有来获得。最终结果是一样的。

于 2009-11-23T21:04:27.420 回答
4

在托管 C++ 中,静态类语法是:-

public ref class BitParser abstract sealed
{
    public:
        static bool GetBitAt(...)
        {
            ...
        }
}

... 迟到总比不到好...

于 2010-06-10T15:35:29.317 回答
4

与其他托管编程语言不同,“静态类”在 C++ 中没有任何意义。您可以使用静态成员函数。

于 2014-01-29T06:34:41.467 回答
3

这类似于 C# 在 C++ 中的处理方式

在 C# file.cs 中,您可以在公共函数中拥有私有 var。当在另一个文件中时,您可以通过使用以下函数调用命名空间来使用它:

MyNamespace.Function(blah);

以下是如何在 C++ 中实现相同的功能:

共享模块.h

class TheDataToBeHidden
{
  public:
    static int _var1;
    static int _var2;
};

namespace SharedData
{
  void SetError(const char *Message, const char *Title);
  void DisplayError(void);
}

共享模块.cpp

//Init the data (Link error if not done)
int TheDataToBeHidden::_var1 = 0;
int TheDataToBeHidden::_var2 = 0;


//Implement the namespace
namespace SharedData
{
  void SetError(const char *Message, const char *Title)
  {
    //blah using TheDataToBeHidden::_var1, etc
  }

  void DisplayError(void)
  {
    //blah
  }
}

其他文件.h

#include "SharedModule.h"

其他文件.cpp

//Call the functions using the hidden variables
SharedData::SetError("Hello", "World");
SharedData::DisplayError();
于 2010-04-26T18:44:39.230 回答
2

一种(众多)替代方案,但(在我看来)最优雅(与使用命名空间和私有构造函数来模拟静态行为相比),在 C++ 中实现“无法实例化的类”行为的方法是使用private访问修饰符声明一个伪纯虚函数。

class Foo {
   public:
     static int someMethod(int someArg);

   private:
     virtual void __dummy() = 0;
};

final如果您使用的是 C++11,则可以通过使用类声明中的说明符来限制其他类继承它,从而确保该类不被继承(纯粹模拟静态类的行为).

// C++11 ONLY
class Foo final {
   public:
     static int someMethod(int someArg);

   private:
      virtual void __dummy() = 0;
};

听起来很愚蠢和不合逻辑,C++11 允许声明“不能被覆盖的纯虚函数”,您可以在声明类的同时使用它final来纯粹和完全实现静态行为,因为这会导致结果类不能被继承,并且虚拟函数不能以任何方式被覆盖。

// C++11 ONLY
class Foo final {
   public:
     static int someMethod(int someArg);

   private:
     // Other private declarations

     virtual void __dummy() = 0 final;
}; // Foo now exhibits all the properties of a static class
于 2020-05-06T01:52:19.243 回答
0

命名空间对于实现“静态类”可能不是那么有用的一种情况是使用这些类来实现组合而不是继承。命名空间不能是类的朋友,因此不能访问类的私有成员。

class Class {
 public:
  void foo() { Static::bar(*this); }    

 private:
  int member{0};
  friend class Static;
};    

class Static {
 public:
  template <typename T>
  static void bar(T& t) {
    t.member = 1;
  }
};
于 2018-04-25T07:47:11.330 回答
0
class A final {
  ~A() = delete;
  static bool your_func();
}

final意味着一个类不能被继承。

delete对于析构函数意味着你不能创建这样一个类的实例。

这种模式也被称为“util”类。

正如许多人所说static class,C++ 中不存在的概念。

namespace在这种情况下,包含static首选作为解决方案的函数的规范。

于 2021-03-01T12:53:56.933 回答
0

There is no such thing as a static class in C++. The closest approximation is a class that only contains static data members and static methods. Static data members in a class are shared by all the class objects as there is only one copy of them in memory, regardless of the number of objects of the class. A static method of a class can access all other static members ,static methods and methods outside the class

于 2021-06-26T07:56:42.827 回答