c ++全局函数vs数据成员函数根据内存

时间:2013-11-21 14:50:19

标签: c++ function global

假设存在一种函数,它既可以是全局函数,也可以作为类成员const函数实现(不是静态,静态是全局)。它取决于用户的选择,如果该函数将被调用或根本不被调用。让我们说 - 在非常特殊的情况下,它通常不被称为或称为稀有。 我对该程序的逻辑组织不感兴趣。我的问题是内存使用情况。我知道数据成员函数花费更多(一个指针更多),因为它通过一个对象调用。如果在运行时没有调用该函数,该怎么办? 如果它是全局函数,它将在程序终身期间驻留在内存中,无论它是否被调用。那么数据成员函数的情况呢,因为它被分别调用通过和动态创建的对象 - 根本没有创建,函数所需的内存,它将被放置的位置以及如果对象不存在会发生什么创造了什么?

感谢。

3 个答案:

答案 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的指针。