2

Let's say that I designed a class like this :

// MyClass.h
#pragma once

class MyClass
{
public:
    const int member = 1; // Since it is a const-integral type var...
    static const int s_member;
    ...
};

then I can access each member like this :

// MyClass.cpp
const int MyClass::s_member = 2; // Initialization of a static member
int main()
{
    MyClass* myClass = new MyClass;
    std::cout << "access to an ordinary member : " << myClass->member << " OR " << (*myClass).member << std::endl;
    std::cout << "access to a static member : " << MyClass::s_member << std::endl;
    
    ...

    return 0;
}

(From what I know, accessing class members by pointer only makes a slight difference: passing an address and dereferencing it.)

Here, what I'm curious about is the difference between this and the way the compiler accesses static members.

I already know that the memory for a static member is located somewhere outside the class instance, rather than inside it. (So every class instances use the same memory for their static members.)

Then, the only difference for accessing static one is "where to access"? Or there exists something beyond it?

Moon
  • 111
  • 7
  • If I understand your question correctly, then no, there is no difference between accessing static members and non-static members other than where they are located. – john Sep 06 '20 at 05:23
  • 1
    Static member variables, static local variables, static global variables and non-static global variables - they all share the common nature of being allocated once (instead of every time a function is called or an object is instantiated), thus having the same memory address throughout the execution of the program. – goodvibration Sep 06 '20 at 05:25
  • 1
    Does this answer your question? [What is the difference between a static and const variable?](https://stackoverflow.com/questions/2216239/what-is-the-difference-between-a-static-and-const-variable) – vhmvd Sep 06 '20 at 05:44
  • 1
    'The compiler', or rather your program at runtime, 'accesses' static members via their fixed addresses, and non-static members via their offset from the current value of `this`. – user207421 Sep 06 '20 at 06:06
  • @Ahmn21 No it doesn't. That question doesn't address non-static non-const members at all. – user207421 Sep 06 '20 at 06:07

1 Answers1

4

First of all, you should realize that a const static member of integer type often won't involve any memory access at all. Unless you use it in a way that (more or less) forces the compiler to do so (e.g., taking its address) this will often (usually?) become simply a symbolic name for a constant. So, given something like:

class foo {
    static const int bar = 1234;
public:
    void baz() { 
        for (int i=0; i<bar; i++)
            // ...
   }
};

...you can normally expect that your i<bar comparison will turn out to be something like:

cmp rcx, 1234

...with no attempt at accessing any variable at all.

A non-const static member is basically a global variable with a kind of funny name. So, at link time the funny name (i.e., the combination of the name of the class and the name of the variable itself) will be mangled (or "decorated", in MS-speak) to produce a unique name for the variable, which will be allocated just like any other global variable. So, semi-representative code might look something like this:

mov rax, foo$$bar
cmp rcx, rax

A non-static variable is allocated on a per-object basis. When a member function is invoked, it receives this as a "hidden" parameter. Inside the function, it can access the variable by adding the appropriate offset, and dereferencing the result to get to the specified variable.

enter image description here

Computing the correct offset is pretty simple for single inheritance, but can get somewhat more complex in the case of something like virtual multiple inheritance.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Thanks, your answer helps me to get an image of it! pop-up question: in the last line, you mentioned "somewhat more complex" for multiply-inherited class. Does this mean that I have to pay an additional performance-cost for it? – Moon Sep 06 '20 at 10:43
  • 1
    @Moon: yes, there's often a performance cost (though it is pretty minor). Basically, just an extra addition (starting from the address of the overall object, do one addition to get to the beginning of a particular base-class sub-object, then another to get to the offset within that object). – Jerry Coffin Sep 06 '20 at 11:03
  • 2
    @Moon • if the performance cost for multiple inheritance is a concern, profile profile profile. Usually hand-rolled alternatives are less performant. Static polymorphism (q.v. CRTP) can be more performant but at the cost of less maintainability and lots of boilerplate thunking and dealing with lots template code. – Eljay Sep 06 '20 at 13:49
  • @Eljay What do you mean "profile"? Since I'm not in an English-speaking country, I cannot infer the meaning of the word in this context, among many of it has. – Moon Sep 07 '20 at 00:39
  • 1
    @Moon • to profile software means to measure its performance of an optimized build. Without profiling, attempts to do performance improvement are just blind guesswork without a basis for actual justification. Compiler optimizers are amazingly good. Profiling software can also tell you were the performance bottlenecks are, so that you don't waste effort on micro-optimizing something that's not important. – Eljay Sep 07 '20 at 02:39