C++中成员函数和非成员函数有什么区别?
一个成员函数(下面称为方法)与一个自由函数(下面称为函数)之间有几个区别。
首先,让我们简单地说一下它们并不是完全不同的。目标代码通常可以编译成 C(或汇编),这些是没有方法概念的过程式语言。方法和函数都像子例程一样被调用。
既然这个问题解决了,让我们来看看它们之间的区别。它们可以分为两类:概念上的和语法上的。
语法上的
语法是任何语言中最显而易见的部分,所以也是最容易处理的部分。
首先要注意:C++ (以及其他一些语言)中有两种不同类型的方法,即 static
方法和普通方法。
两种类型的方法都可以完全访问类内部(protected
和 private
部分),当然还可以访问类的 public
接口。
static
方法等同于 friend
函数(除了某些作用域上的差异)。
在普通方法中,一个特殊关键字(例如 C++ 中的 this
)允许访问调用该方法的当前对象(通过 .
、->
、.*
或 ->*
运算符)。
普通方法可以是 const
和/或 volatile
限定的,使其适用于(分别是)const
和/或 volatile
限定的对象。例如,不能在 const
对象上调用非 const
方法。这可以看作在方法内对 this
加以限定,即 void Foo::bar() const
具有类型为 Foo const*
的 this
。
一个普通方法可以被标记为 virtual
。虚拟性通过启用重写来实现运行时多态性。这个机制在这里不再详细说明,让我们注意到函数不能是虚函数。
常常忽略的一点是,方法(包括静态和普通方法)是在类内部范围内声明的。这对于名称查找(其他方法或属性/变量的查找)非常重要,因为它意味着从类外部声明的元素中进行查找时,类的元素具有优先权。
由于在属性或方法之前使用this->
限定符并不是强制性的,在普通方法中很方便,但可能会引入微妙的错误。在静态方法中,它避免了通过类名限定要访问的静态属性和方法。
既然主要的语法差异已经确定,那么让我们检查概念上的差异。
从概念上讲
OOP通常是关于将状态和行为(针对该状态)联系在一起的。这是通过创建类来完成的,该类组合属性(状态)和行为(方法),并(理论上)声称只有方法才能作用于状态。因此,在OOP中,方法负责实现类的行为。
这些方法参与封装状态(使客户端摆脱实现细节)和保持类不变式(有关类状态的语句,从其诞生到其消亡始终成立,无论您对其做什么)。
C++
如我们之前所见,在C++中,这是通过使用不同的访问级别(public
,protected
和private
)并将非公共级别的访问权限授予受限制的代码部分来完成的。通常情况下,属性将是私有的,因此只能由类方法(以及一些朋友,用于语法怪癖)访问。
注意:我敦促您不要使用protected
属性,因为很难追踪它们的修改,并且由于派生类的集合是无限的......它们的实现不能容易地改变。
不过要注意,C++ 不鼓励在接口中添加过多的方法。
问题在于,由于方法负责维护不变量,方法越多,责任就会分散,导致更难追踪错误和确保正确性。此外,由于方法依赖于类内部,所以改动成本更高。
相反,在 C++ 中,通常建议编写最少量的方法,并将其余行为委托给非friend
函数(只要不增加太多成本)。
std::string
的看法。这个答案变得有点啰嗦了,但我怀疑我可能忽略了其他人认为关键的差异...... 哦好吧。
非静态的成员函数有一个隐式的this
参数,而非成员函数则没有。
语法上,在.
或->
操作符的左侧传递这个隐式参数,例如like.so()
或like->so()
,而不是作为函数参数so( like )
。
同样地,当声明一个成员函数时,你需要在它所属的类中进行声明:
class Class {
public:
void a_public_member_function();
};
非成员函数在任何类外声明(C++称之为“命名空间范围”)。
(非静态) 成员函数也可以是 虚函数,但非成员函数和静态成员函数不行。
在一个类的对象上调用非静态成员函数,该函数会隐式地拥有代表当前对象的this
指针,通过这个指针,它可以轻松访问其他成员并具有完全访问权限(即访问private
成员)。
非成员函数没有隐式的this
指针。在下面的示例中,bar
是成员函数,而freebar
不是。它们两个或多或少做相同的事情,但请注意bar
如何通过this
获取隐式对象指针(仅bar
具有访问foo
成员的特权,freebar
只能访问公共成员)。
class foo {
public:
void bar() {
this->x = 0; // equivalent to x = 0;
}
int x;
};
void freebar(foo* thefoo) {
thefoo->x = 1;
}
// ...
foo f;
f.bar();
// f.x is now 0
freebar(&f);
// f.x is now 1
从语义上讲,成员函数不仅仅是一个具有隐式this参数的函数。它旨在定义对象的行为(例如,汽车对象将具有drive(), stop()作为成员函数)。
请注意,还存在静态成员函数,它们具有完全的权限,但不会得到隐式的"this",也不通过类的实例调用(而是通过类的完整名称进行调用)。
this
是语言实现的遗留问题。个人认为应该强调方法作用于特定对象的关系,而不是关注如何实现这种关系。在99.9%的情况下,您实际上并不使用 this
,但仍然与对象的成员进行交互(我唯一使用它的地方是在 operator= 中返回对自身的引用)。 - Martin Yorkthis
。 - Johannes Schaub - litbstatic
成员函数的注释。 - Alexander Gesslerf()
是Sample
类的成员函数,而g()
是非成员函数:class Sample
{
void f();
};
void g();
很简单。由于f()
是类Sample
的成员,因此它被称为类Sample
的成员函数。而由于g()
不属于任何类,因此它被称为非成员函数。
对象上调用成员函数并可以访问类的字段。
成员函数可以通过virtual
关键字实现多态性,这对于面向对象编程是必不可少的。