0

Let's assume there is a kind of a function, which could be both global function or could be implemented as class member const function (not static, static means global). It depends on user's choice if the function will be called or not called at all. Let say - It is usually not called or called rare - in very special cases. I am not interested of the program's logic organization. My question is about the memory usage. I know that data member function costs a little bit more (one pointer more), since it is called trough an object. What about the case if the function is not called in runtime? If it is global function it will be resident in memory during program's lifelong no matter it is called or not. What about the case of data member function, since it is called through and object that is dynamically created, respectively - not created at all, what about the memory needed for the function, where it will be placed and what happens if an object is not created at all?

Thanks.

3 Answers3

1

In typical C++ implementations:

  • A function is a function, regardless of whether it's a member function of some class, and it will reside in memory "forever" (disregarding demand-paging and all that).

  • A non-virtual member function is nothing more than an ordinary function that takes a hidden argument called this.

  • A virtual member function is a member function that has its address recorded in a dispatch table for the class that it belongs to.

So member functions are very much like ordinary functions, the main thing that changes is the way the call is done: with an additional argument, and possibly through a hidden function pointer.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
1

As was mentioned in the comments, a class member function in C++ can be thought of as a normal C-style function which implicitly takes as an argument a pointer to the instance of the class from which it is called. For example, consider the following C++ code:

class foo {
 public:
  void set(int j);
 private:
  int i;
};

void foo::set(int j) {
  i = j;
}

The function foo::set can be thought of a C-style function which takes a (hidden) argument of type class foo *, and when you do foo a; a->set(3);, the class foo * that is (implicitly) passed is &a.

In summary, whether or not you ever call foo::set, it will be loaded into memory. (The only possible exception is if there are no calls to the function in your code at all, in which case an optimizer may remove it during compilation, but if it is possible to call it dynamically based on user input then it will be loaded.) On the other hand, no matter how many instances of class foo you have, only one copy of foo::set ever needs to exist in memory.

Andrey Mishchenko
  • 3,986
  • 2
  • 19
  • 37
1

If your function isn't virtual then there is no overhead of being a member function (aka method).

In fact, in generated object code there is no such notion of a "member function" (unless it's virtual, more on that later). All methods are functions with special variable this. Compiler at the compile time knows what method is to be called at the run time, so it can generate method-specific object code (with some handling of this). By the way, that's the case why the following code doesn't crash:

#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;
}

Despite of null pointer there, programs successively finishes and prints "Hey, I'm still alive!". And that's okay if you think about method wouldItCrash as some kind of a function with special parameter 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;
}

We don't dereference the pointer in function, so no problems happen.

Consider the following simple program:

#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;
}

If you compile it without optimizations (just g++ main.cpp) and then look at the symbols table (nm a.out), you'll see

0804876d T _Z14FunctionName__v
...
0804882c W _ZN11ClassName__13MethodName1__Ev
08048858 W _ZN11ClassName__13MethodName2__Ev

That is, all methods became just plain functions with special names (so no conflict could happen, see name mangling)

Virtual functions

As I said earlier, there are some special rules applying to the virtual functions. Virtual functions cannot be resolved at the compile-time (compiler doesn't know which instance of derived class will be processed at the run-time), so it's delayed to the run-time. So in order to serve virtual functions, each class (with that kind of functions, of course) has a special lookup table named virtual method table (aka vtable). In that case you clearly pay some memory space: you need a pointer to the function in the vtable and a pointer to the vtable for each instance of your class.

Community
  • 1
  • 1
Artem Sobolev
  • 5,891
  • 1
  • 22
  • 40