c++ - 非成员非友元函数语法

标签 c++ class-design

他们是否可以使用与成员函数相同的“点”表示法在对象上使用非成员非友元函数?

我能否将(任何)成员从类中拉出来,让用户像往常一样使用它?

更长的解释:

Scott Meyers , Herb Sutter 等人认为非成员非友元函数是对象接口(interface)的一部分,并且可以改进封装。我同意他们的看法。

然而,在最近阅读这篇文章之后:http://www.gotw.ca/gotw/084.htm我发现自己在质疑语法含义。

在那篇文章中,Herb 提议使用一个inserterasereplace 成员,以及几个非成员非友元函数同名。

这是否意味着,如我所想,Herb 认为某些函数应该与点符号一起使用,而其他函数应作为全局函数使用?

std::string s("foobar");

s.insert( ... ); /* One like this */
insert( s , ...); /* Others like this */

编辑:

感谢大家提供非常有用的答案,但是,我认为我的问题的要点被忽略了。

我特别没有提到运算符的具体情况,以及它们如何保留“自然”符号。也不应该将所有内容包装在 namespace 中。这些东西写在我链接的文章中。

问题本身是:

在文章中,Herb 建议一个 insert() 方法是成员函数,而其余的是非成员非友元函数。

这意味着要使用一种形式的 insert(),您必须使用点表示法,而对于其他形式,则不需要。

这只是我,还是听起来很疯狂?

我有一种预感,也许您可​​以使用一种语法。 (我在想 Boost::function 如何为 mem_fun 使用 *this 参数)。

最佳答案

是的,就是说一个对象的部分接口(interface)是由非成员函数组成的。

你是对的,它涉及对类 T 的对象使用以下符号:

void T::doSomething(int value) ;     // method
void doSomething(T & t, int value) ; // non-member non-friend function

如果您希望 doSomething 函数/方法返回 void,并有一个名为“value”的 int 参数。

但有两件事值得一提。

首先是类接口(interface)的函数部分应该在同一个命名空间中。这是使用命名空间的另一个原因(如果需要另一个原因的话),如果只是为了“组合”一个对象和作为其接口(interface)一部分的函数。

好的部分是它促进了良好的封装。但不好的是它使用了类似函数的符号,我个人非常不喜欢。

二是运营商不受此限制。例如,类 T 的 += 运算符可以用两种方式编写:

T & operator += (T & lhs, const T & rhs) ;
{
   // do something like lhs.value += rhs.value
   return lhs ;
}

T & T::operator += (const T & rhs) ;
{
   // do something like this->value += rhs.value
   return *this ;
}

但这两种表示法都用作:

void doSomething(T & a, T & b)
{
   a += b ;
}

从美学的角度来看,这比类函数符号要好得多。

现在,如果能够从同一个接口(interface)编写一个函数,并且仍然能够通过“.”调用它,那将是一个非常酷的语法糖。符号,就像在 C# 中一样,如 michalmocny 所提到的。

编辑:一些例子

假设我出于某种原因想要创建两个“类整数”类。 第一个是 IntegerMethod:

class IntegerMethod
{
   public :
      IntegerMethod(const int p_iValue) : m_iValue(p_iValue) {}
      int getValue() const { return this->m_iValue ; }
      void setValue(const int p_iValue) { this->m_iValue = p_iValue ; }

      IntegerMethod & operator += (const IntegerMethod & rhs)
      {
         this->m_iValue += rhs.getValue() ;
         return *this ;
      }

      IntegerMethod operator + (const IntegerMethod & rhs) const
      {
         return IntegerMethod (this->m_iValue + rhs.getValue()) ;
      }

      std::string toString() const
      {
         std::stringstream oStr ;
         oStr << this->m_iValue ;
         return oStr.str() ;
      }

   private :
      int m_iValue ;
} ;

这个类有 6 个方法可以访问它的内部。

第二个是 IntegerFunction:

class IntegerFunction
{
   public :
      IntegerFunction(const int p_iValue) : m_iValue(p_iValue) {}
      int getValue() const { return this->m_iValue ; }
      void setValue(const int p_iValue) { this->m_iValue = p_iValue ; }

   private :
      int m_iValue ;
} ;

IntegerFunction & operator += (IntegerFunction & lhs, const IntegerFunction & rhs)
{
   lhs.setValue(lhs.getValue() + rhs.getValue()) ;
   return lhs ;
}

IntegerFunction operator + (const IntegerFunction & lhs, const IntegerFunction & rhs)
{
   return IntegerFunction(lhs.getValue() + rhs.getValue()) ;
}

std::string toString(const IntegerFunction & p_oInteger)
{
   std::stringstream oStr ;
   oStr << p_oInteger.getValue() ;
   return oStr.str() ;
}

它只有 3 个方法,因此减少了可以访问其内部的代码量。它有3个非成员(member)非好友功能。

这两个类可以用作:

void doSomething()
{
   {
      IntegerMethod iMethod(25) ;
      iMethod += 35 ;
      std::cout << "iMethod   : " << iMethod.toString() << std::endl ;

      IntegerMethod result(0), lhs(10), rhs(20) ;
      result = lhs + 20 ;
      // result = 10 + rhs ; // WON'T COMPILE
      result = 10 + 20 ;
      result = lhs + rhs ;
   }

   {
      IntegerFunction iFunction(125) ;
      iFunction += 135 ;
      std::cout << "iFunction : " << toString(iFunction) << std::endl ;

      IntegerFunction result(0), lhs(10), rhs(20) ;
      result = lhs + 20 ;
      result = 10 + rhs ;
      result = 10 + 20 ;
      result = lhs + rhs ;
   }
}

当我们比较运算符的使用(“+”和“+=”)时,我们发现将运算符设为成员或非成员在其表面用法上没有区别。尽管如此,还是有两个区别:

  1. 成员可以访问其所有内部信息。非成员必须使用公共(public)成员方法

  2. 对于某些二元运算符,如 +、*,进行类型提升很有趣,因为在一种情况下(即 lhs 提升,如上所示),它不适用于成员方法。

现在,如果我们比较非运算符使用(“toString”),我们会发现对于类 Java 开发人员来说,成员非运算符使用比非成员函数更“自然”。尽管不熟悉,但对于 C++ 来说,重要的是要接受这一点,尽管它有语法,但从 OOP 的角度来看,非成员版本更好,因为它无法访问类内部。

作为奖励:如果你想添加一个运算符(resp. 一个非运算符函数)到一个没有运算符的对象(例如, 的 GUID 结构),那么你可以,而不需要修改结构本身。对于运算符(operator)来说,语法是自然的,而对于非运算符(operator)来说,嗯......

免责声明:当然这些类是愚蠢的:set/getValue 几乎可以直接访问其内部结构。但是用字符串替换整数,正如 Herb Sutter 在 Monoliths "Unstrung" 中提出的那样,你会看到一个更真实的案例。

关于c++ - 非成员非友元函数语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/332460/

相关文章:

.net - 如何从企业应用程序的类设计开始?

c++ - 初始化另一个类对象中的对象。(在该构造函数上执行一些操作之后。)

c++ - `const` 关键字在函数之后有什么作用?

swift - 是否可以通过引用而不是副本将数组分配给类属性?

oop - 封装的对象应该是公共(public)的还是私有(private)的?

c++ - 将数组传递给函数 C++

c++ - 用于类型转换空指针的类型/类 vector

c++ - 访问使用 pinned_allocator 分配的 host_vector 项(推力库)

c++ - 将 QFile 转换为文件*

另一个类中类对象的C++析构函数