假设存在一种函数,它既可以是全局函数,也可以作为类成员const函数实现(不是静态,静态是全局)。它取决于用户的选择,如果该函数将被调用或根本不被调用。让我们说 - 在非常特殊的情况下,它通常不被称为或称为稀有。 我对该程序的逻辑组织不感兴趣。我的问题是内存使用情况。我知道数据成员函数花费更多(一个指针更多),因为它通过一个对象调用。如果在运行时没有调用该函数,该怎么办? 如果它是全局函数,它将在程序终身期间驻留在内存中,无论它是否被调用。那么数据成员函数的情况呢,因为它被分别调用通过和动态创建的对象 - 根本没有创建,函数所需的内存,它将被放置的位置以及如果对象不存在会发生什么创造了什么?
感谢。
答案 0 :(得分:1)
在典型的C ++实现中:
函数是一个函数,无论它是否是某个类的成员函数,它将“永远”驻留在内存中(忽略请求分页和所有这些)。
非virtual
成员函数只不过是一个普通函数,它接受一个名为this
的隐藏参数。
virtual
成员函数是一个成员函数,其地址记录在它所属的类的调度表中。
因此,成员函数非常类似于普通函数,主要的变化是调用的方式:使用附加参数,可能还有隐藏的函数指针。
答案 1 :(得分:1)
正如在注释中提到的,C ++中的类成员函数可以被认为是一个普通的C风格函数,它隐式地将一个指针作为参数,该指针指向调用它的类的实例。例如,请考虑以下C ++代码:
class foo {
public:
void set(int j);
private:
int i;
};
void foo::set(int j) {
i = j;
}
函数foo::set
可以被认为是一个C风格的函数,它接受class foo *
类型的(隐藏)参数,当你执行foo a; a->set(3);
时,class foo *
(隐式)传递的是&a
。
总之,无论您是否曾致电foo::set
,它都会被加载到内存中。 (唯一可能的例外是,如果代码中根本没有对函数的调用,在这种情况下,优化器可能会在编译期间将其删除,但如果可以根据用户输入动态调用它,那么它将被加载。 )另一方面,无论你有多少class foo
个实例,内存中只需要存在foo::set
的一个副本。
答案 2 :(得分:1)
如果你的函数不是虚函数,那么成为函数(aka方法)就没有开销。
事实上,在生成的对象代码中没有“成员函数”的概念(除非它是虚拟的,稍后会更多)。所有方法都是具有特殊变量this
的函数。编译时编译器知道在运行时调用什么方法,因此它可以生成特定于方法的对象代码(对this
进行一些处理)。顺便说一句,这就是为什么以下代码不会崩溃的原因:
#include <iostream>
class Crashy {
public:
void wouldItCrash() {
std::cout << "Hey, I'm still alive!" << std::endl;
}
};
int main() {
Crashy* ptr = 0;
ptr->wouldItCrash();
return 0;
}
尽管那里有空指针,程序依次完成并打印"Hey, I'm still alive!"
。如果您将方法wouldItCrash
视为具有特殊参数this
的某种函数,那也没关系:
#include <iostream>
void Crashy__wouldItCrash(Crashy *this) {
std::cout << "Hey, I'm still alive!" << std::endl;
}
int main() {
Crashy* ptr = 0;
Crashy__wouldItCrash(ptr);
return 0;
}
我们不会在函数中取消引用指针,因此不会发生任何问题。
考虑以下简单程序:
#include <iostream>
void FunctionName__() {
std::cout << "Hello from function!" << std::endl;
}
class ClassName__ {
public:
void MethodName1__() {
std::cout << "Hello from method 1!" << std::endl;
}
void MethodName2__() {
std::cout << "Hello from method 2!" << std::endl;
}
};
int main() {
FunctionName__();
ClassName__ a;
a.MethodName1__();
a.MethodName2__();
return 0;
}
如果你在没有优化的情况下编译它(只是g++ main.cpp
),然后查看符号表(nm a.out
),你会看到
0804876d T _Z14FunctionName__v
...
0804882c W _ZN11ClassName__13MethodName1__Ev
08048858 W _ZN11ClassName__13MethodName2__Ev
也就是说,所有方法都变成了具有特殊名称的普通函数(因此不会发生冲突,请参阅name mangling)
虚拟功能
正如我之前所说,有一些适用于虚函数的特殊规则。虚函数无法在编译时解析(编译器不知道在运行时将处理哪个派生类实例),因此它会延迟到运行时。因此,为了提供虚函数,每个类(当然具有这种函数)都有一个名为virtual method table(又名vtable)的特殊查找表。在这种情况下,你明确地支付了一些内存空间:你需要一个指向vtable中函数的指针和一个指向你的类的每个实例的vtable的指针。