在 C++ 成员函数中使用“静态”关键字限制访问

Posted

技术标签:

【中文标题】在 C++ 成员函数中使用“静态”关键字限制访问【英文标题】:Using "Static" Keyword to Limit Access in C++ Member Functions 【发布时间】:2011-04-21 11:33:08 【问题描述】:

我知道拥有静态成员函数的一个好处是不必初始化一个类来使用它们。在我看来,它们的另一个优点可能是无法直接访问类的非静态内容。

例如,一种常见的做法是,如果您知道函数将具有不可更改的参数,则只需将这些参数标记为常量。例如:

bool My_Class::do_stuff(const int not_to_be_changed_1,
                        std::vector<int> const * const not_to_be_changed_2)

  //I can't change my int var, my vector pointer, or the ints inside it.

那么使用静态成员函数来限制访问是否有效。例如,假设你有一个函数

void My_Class::print_error(const unsigned int error_no) 
  switch (error_no) 
    case 1:
      std::cout << "Bad read on..." << std::endl;
      break;
    //...
    default:
      break;
  

这里我们不会访问类的任何成员变量。因此,如果我将功能更改为:

static void My_Class::print_error(const unsigned int error_no) 
  switch (error_no) 
    case 1:
      std::cout << "Bad read on..." << std::endl;
      break;
    //...
    default:
      break;
  

如果我无意中尝试访问我的私有变量等,我现在会收到一个错误(除非我将我的类的实例传递给自己,这将是有目的的^_^!)

这是一种有效的技术吗,类似于主动将不应更改的 args 设置为常量?

它在效率或使用方面可能有什么缺点?

我问的主要原因是我阅读的大多数“静态”教程都没有提到以这种方式使用它,所以我想知道是否有充分的理由不这样做,因为它似乎是一个有用的工具.


编辑 1:此用途的进一步逻辑理由:

如上所述,我有一个函数 print_error。我可以使用命名空间:

namespace MY_SPACE 
   static void print_error(...) 
      ...
    

   class My_Class 
      ....
      void a(void)
   

但这很痛苦,因为我现在必须延长所有 var 声明,即

MY_SPACE::My_Class class_1;

all 从我的类中删除一个函数,它本质上是我的类的成员。

当然,函数有多个级别的访问控制:

//can't change pointer to list directly
void My_Class::print_error(std::vector<int> const * error_code_list) ...

//can't change pointer to list or list members directly
void My_Class::print_error(std::vector<int> const * const error_code_list) ...

//can't change pointer to list or list members directly, access
//non-const member vars/functions
void My_Class::print_error(std::vector<int> const * const error_code_list) const ...

//can't change pointer to list or list members directly, access
//non-static member vars/functions
static void My_Class::print_error(std::vector<int> const * const error_code_list) ...

//can't change pointer to list or list members directly, access
//member vars/functions that are not BOTH static and const
static void My_Class::print_error(std::vector<int> const * const error_code_list) const ...

当然这有点不典型,但使用 const 函数和 const 变量的程度有所降低。我见过很多例子,人们可以使用 const 函数,但没有。然而,有些人认为这是个好主意。我认识许多不了解 const 函数或静态函数的含义的 C++ 初学者。同样,很多人都会理解两者。

那么,如果语言/规范允许像使用 const 函数等那样使用它,那么为什么有些人如此坚决地反对将其用作访问控制机制?

【问题讨论】:

如果你要拒绝它对成员的访问权限,那么将函数作为一个类的成员似乎很奇怪。 限制访问的更好方法是只使用非成员函数。不过,我真的不明白你要保护什么。 假设你有一个成员函数,你知道目前不需要访问成员变量/函数,但与类的工作/函数有关。从概念上讲,将其包含在类中是有道理的,但是您要确保没有类成员的访问权限。所以你可以让它成为一个静态函数,就像你要创建一个你想要阻止访问const 变量的变量一样。当然你可以故意不接触变量/成员,因此不使用const/static,但许多人选择谨慎行事,至少在使用const时会犯错。 @Jason R. Mick:我可以理解创建函数const 以防止它改变成员。但是如果函数不能访问成员,那么已经有一个机制:不要让它成为成员。我同意您应该在安全方面犯错,但在我看来, const 函数是安全方面和惯用的选择。人们不希望将静态用于访问控制,因为这不是它的预期目的。 我认为任何执行类独有功能的东西都是成员,即使它不依赖于类的其他部分。 【参考方案1】:

可以公平地说,全局作用域函数、静态成员函数和友元函数彼此之间并不完全正交。在某种程度上,这主要是因为它们旨在对程序员具有不同的语义含义,即使它们产生相似的输出。

特别是静态成员方法和友元函数唯一的区别就是命名空间不同,静态成员的命名空间为::className::methodName,友元函数只是::friendFunctionName。它们都以相同的方式运行。

嗯,实际上还有另一个区别,静态方法可以通过指针间接访问,这在多态类的情况下很有用。

所以问题是,函数是否属于类的“part”?如果是这样,请使用静态方法。如果没有,则将该方法放在全局范围内,如果它可能需要访问私有成员变量,则将其设为好友(如果不需要,则不要)

【讨论】:

【参考方案2】:

当静态成员函数与类相关但不对类的实例进行操作时,应使用它们。

示例包括一类实用程序方法,所有这些都是静态的,因为您永远不需要实用程序类本身的实际实例。

另一个例子是一个使用静态辅助函数的类,这些函数对于类外的其他函数已经足够有用了。

【讨论】:

因此,根据您的回复,它听起来像我最初断言的示例用例(辅助错误打印机功能)将是您心目中static 的有效候选者。 @Jason:是的。当然,我相信实际上 所有 函数应该是类成员(静态或非静态)。裸函数打破了面向对象的特性(尽管我意识到非成员模板函数作为辅助“粘合”函数很有用)。【参考方案3】:

不要这样做。使用static 作为访问控制机制是一种野蛮可憎的行为。

不这样做的一个原因是它很奇怪。维护程序员将很难理解您的代码,因为它太奇怪了。可维护的代码是好的代码。每个人都会得到const 方法。没有人收到static-as-const。代码的最佳文档是代码本身。自我记录代码是您应该追求的目标。不是让您不必编写 cmets,而是让 they 不必read 它们。因为你知道他们无论如何都不会这样做。

不这样做的另一个原因是你永远不知道未来会发生什么。您上面的print_error 方法现在不需要访问类的状态。但我可以看到有一天可能需要它。假设您的类是 UDP 套接字的包装器。在会议进行到一半的时候,另一端砰的一声关上了门。你想知道为什么。您发送或接收的最后一条消息可能会提供线索。你不应该把它扔掉吗?你需要状态。

这样做的一个错误原因是它提供了成员访问控制。是的,它会这样做,但已经有这样的机制。假设您正在编写一个要确保不会更改对象状态的函数。例如,print_error 不应该改变任何对象的状态。所以制作方法const:

class MyClass

public:
  void print_error(const unsigned int error_no) const;
;

...

void MyClass::print_error(const unsigned int error_no) const

  // do stuff

print_error 是一个const 方法,实际上意味着this 指针是const。您不能更改任何非mutable 成员,也不能调用任何非const 方法。这不正是你想要的吗?

【讨论】:

但是为什么规范允许static 成员函数,如果不是因为它被保留给不需要依赖其他类成员或使用类变量的辅助函数(除了来自辅助函数库,也许......)? 在我题为“编辑 1”的新部分中查看我上面对不同访问级别的描述。 @Jason:因为有时您需要一个类级别的方法,而不是一个实例级别的方法。我想到了两个例子:回调函数和工厂方法。 @Jason R. Mick:用于执行适用于所有实例的操作,或无特定实例,其中不需要特定实例。当较低级别的 C API 需要回调函数时也经常需要。【参考方案4】:

任何成员函数都应该可以访问对象的其他成员。你为什么要保护自己免受自己的伤害?

静态成员通常很少使用,例如工厂方法。您将创造一种情况,使下一个使用您的代码的人“WTF???”

【讨论】:

好吧,假设您有一个成员函数,您绝对不需要访问任何其他成员函数/变量。那么使用静态限制访问不是合适的吗?我知道这听起来很奇怪,但是使用您可能需要访问权限以及代码更改的某个时间点的参数通常可以用作针对常量的参数。 您经常想保护自己免受自己的伤害。这就是 const 存在(或 final 或 readonly)的原因。这些修饰符表示意图,并防止您以非预期的方式使用数据。此外,对于像“print_error”这样的函数,我宁愿将其设为静态并传入输出流,然后隐式使用绑定到特定对象的输入流。 关于下一个查看您的代码的人的反应,如果您正确记录代码(您将其设为静态的原因,如上所述),您的同事/同事应该理解。就像 const 一样。当我看到一个 const 或一个静态函数时,我试图理解为什么这个人做出了这种区分。最好的情况是他们留下文档说明原因。

以上是关于在 C++ 成员函数中使用“静态”关键字限制访问的主要内容,如果未能解决你的问题,请参考以下文章

C++学习笔记.关键字static&静态成员变量

C++学习笔记.关键字static&静态成员变量

C++学习笔记.关键字static&静态成员变量

C++类中的静态成员函数以及静态成员变量

C++类中的静态成员函数以及静态成员变量

C++入门篇之类和对象总结