8

As far as I know each created object has its own address, and each object's method also has its own address. I want to verify that with the following idea:

Step 1: Build class A with public method, its name is "method".

Step 2: Create two objects in class A, they are object "b" and object "c".

Step 3: Access the addresses of "b.method" and "c.method" to check that they are equal by using a function pointer.

But I met the problem in step 3 and have found every way to solve but failed. So I posted up here to ask people to help me how to verify what I said above. Thanks everyone! And here is my C++ code:

#include<iostream>
using namespace std;
class A
{
  public:
     int a;
     void method()
     {
       //do something
     }
     static void (*fptr)();
};
int main()
{
    A b, c;

    A::fptr= &(b.method);  //error: cannot convert 'A::method' from type 
                           // 'void(A::)()' to type 'void (*)()'
    cout << A::fptr << endl;
    A::fptr= &(c.method);  //error: cannot convert 'A::method' from type  
                           //'void(A::)()' to type 'void (*)()'
    cout << A::fptr << endl;
    return 0;
}
TaQuangTu
  • 2,155
  • 2
  • 16
  • 30
  • 2
    **member functions** are not like a regular function that you can access indirectly via a regular function pointer. – StoryTeller - Unslander Monica Sep 13 '17 at 06:53
  • 2
    You need member function pointers: `void (A::*fptr)();` – HolyBlackCat Sep 13 '17 at 06:53
  • 4
    And checking that it's the same function is moot. Of course it is, compilers aren't stupid. – StoryTeller - Unslander Monica Sep 13 '17 at 06:54
  • 1
    You cannot access an address of a member function. You can have a pointer-to-member, but unlike regular pointers, pointers-to-members don't store addresses. A pointer-to-member is created with the `&A::method` syntax. An object of type `A` is not needed and cannot be used here. You can check that `&A::method == &A::method` until cows come home, but it will reveal very little. – n. m. could be an AI Sep 13 '17 at 07:05
  • A pointer-to-member is a pointer in a more abstract sense than "address". – molbdnilo Sep 13 '17 at 07:16
  • `void(A::*fp)() = &(b.method);` doesn't work either *(error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say '&A::method')*. `void(A::*fp)() = &A::method;` works. – starriet Feb 22 '23 at 02:51

4 Answers4

4

Member functions are not like typical functions. The main difference is the way they are called (they have an implicit this argument), but that difference is enough for the language to demand a new way of defining pointers to them. See here for more details.

The following code prints the address in memory of a method:

#include <iostream>

class A {
public:
    void method() {
    }
};

int main() {
    auto ptr = &A::method;
    std::cout << reinterpret_cast<void*>(ptr) << "\n";

    return 0;
}

As you can see, I had to cast the pointer to a void* to fool the compiler. G++ prints out a warning on that line, but otherwise does what you want with it.

Notice that the type of ptr is void (A::*)(), i.e. "a pointer to a method in A that receives no arguments and returns void". A pointer to methods in your B and C may be slightly different. They should convert to pointers to A, so you might want to go through that when comparing (or just cast to void* and ignore the warning).

Edited to add: It seems no cast is needed for comparison. You can just directly compare the two pointers to methods, and they will return true or false correctly.

Shachar Shemesh
  • 8,193
  • 6
  • 25
  • 57
3

Thank you everyone! I've been wondering about this for a long time, and now I've figured out the answer myself, there's only one "method()" that's created on memory, even if there are hundreds of objects created. All objects created that want to use this method will have to find the address of this method. Here is the code to prove what I said:

#include<iostream>
using namespace std;
class A
{
 public:
    int a;
    void method()
    {
       //do something
    }
    static void (*fptr)();
};
int main()
{
    A b,c;
    if(&(b.method)==&(c.method))
    {
        cout<<"they are same\n";
    }
    else
    {
        cout<<"they are not same\n";
    }
    return 0;
}
TaQuangTu
  • 2,155
  • 2
  • 16
  • 30
  • 1
    Is this working? I get a compilation error. Is address of b.method known at compile time? How did you compile this? I was using `clang++ --std=c++20 -Wall -g class-method-stackoverflow-1.cpp && ./a.out` – dragonfry Oct 09 '22 at 14:02
  • @dragonfry it's has been over five years and the compiler might have been changed. You can try this code to verify my answer: https://ideone.com/ANT2X6 – TaQuangTu Oct 10 '22 at 02:48
  • This doesn't compile because of `&(b.method)`, and [your new example](https://ideone.com/ANT2X6) itself does **not** mean the actual addresses are the same. If we consider *pointers to data members* instead of *pointers to member functions*, the values can be different between different objects but still the values of the pointers to data members are the same(like '1', not address). [example](https://wandbox.org/permlink/yX3YmkRk5jR6JZJT). @n. m. 's comment also explains this. – starriet Feb 22 '23 at 05:10
1

The compiler and linker does not have to give distinct functions, distinct implementations.

On at least some platforms, the compiler will spot that 2 functions have the same implementation, and merge the 2 functions into a single piece of code. That limits the amount of bloat added by the template system, but stops it being a guaranteed behavior to identify different member functions.

The compiler can

  • inline all the examples of a single piece of code, and the result is it doesn't have an address.
  • share implementations where the code is the same.
  • create multiple implementations of the same function if it thinks it can be done faster.

When C++ was invented, there was a lot of effort to ensure that a C++ compilation unit was able to call a C compilation unit, and the result of this effort, was that many items of the C++ implementation became visible using compatibility tricks.

The C++ pointer to member function had no backwards-compatibility baggage, and thus no reason to allow it to be inspected. As such it is an opaque item, which can be implemented in multiple ways.

mksteve
  • 12,614
  • 3
  • 28
  • 50
  • Sharing implementation and having the same pointer are not the same. The specification specifies that pointer to non-virtual member functions compare equal if and only if they refer to the same member (Equality operators in C++11), and function only if they point to the same function. Not "same function or a function that behaves the same". – Hans Olsson Sep 13 '17 at 08:11
  • @HansOlsson From this answer https://stackoverflow.com/questions/26533740/do-distinct-functions-have-distinct-addresses there seems to be evidence that compilers do fold implementation. It is unclear as to whether taking the address of a function, or adding a comparison creates a broken implementation or forces the compiler to not make such an optimization. – mksteve Sep 13 '17 at 08:47
  • Folding implementations is ok in itself. And the conclusion at the end of the first answer is that it is incorrect if that influences pointer comparison: "It is not conforming to turn two functions to have same address ... Taking the address of a function is observable behavior and therefore folding identical functions would violate the as-if rule. " – Hans Olsson Sep 13 '17 at 09:11
0

In your example there is only one copy of the method in memory. But i cannot think of any easy way to verify that. You can make thousands of objects and see the memory consumption. You can explore the memory occupied by your object in debugger. The memory consumption may be affected by operating system strategy for assigning memory to process. You can also explore disassembly at https://gcc.godbolt.org/ Relevant start for you would be https://godbolt.org/g/emRYQy

raven
  • 11
  • 3