This pointer in C++ class
Toc
Usages
- How to use this pointer
- How to check an instance is correctly created
- How to use a function pointer of a class
Explain
Class Structure
When a class becomes an instance, the instance is allocated in memory. Code 1 is a simple example code, and Figure 1 shows how they are in memory.
// 01_C_testSimpleExample.cpp
#include <iostream>
using namespace std;
class Point
{
int x;
int y;
public:
void set(int a, int b)
{
x = a;
y = b;
cout << "a: " << a << ", b: " << b << endl;
return;
}
};
int main()
{
Point p1;
Point p2;
p1.set(10, 20);
p2.set(30, 40);
return 0;
}
// Compile: clang ++ -std=c++14 -o 01_C_testSimpleExample 01_C_testSimpleExample.cpp
a: 10, b: 20
a: 30, b: 40
Each instance has their own member variables in memory. However, member functions are shared among the instances. Instead, C++ translates the member functions to add this pointer, like Code 2.
// 02_N_testMember.cpp
void Point_set(Point* this, int a, int b)
{
this->x = a;
this->y = b;
return;
}
// Compile: Not available
How about a static member function? A static member function can be called without an instance, so it may has different mechanism. A static member function does not have an argument for this pointer, but each member variables have this. Therefore, Code 3 has an error.
// 03_N_testStaticMember.cpp
#include <iostream>
using namespace std;
class Point
{
int x;
int y;
public:
static void foo(int a)
{
x = a; // = this->x = a
// However, this is not existed in this static function.
return;
}
};
int main()
{
Point::foo(10);
return 0;
}
// Compile: Not available
In this case, x is converted to this->x. However, the static foo() does not have this pointer, so it cannot be compiled.
Checking an Instance is Correctly Created
Sometimes, it is necessary to check an instance is created correctly before using it. To do that, this kind of checking is used.
// 04_C_testCallCheck.cpp
#include <iostream>
using namespace std;
class Sample
{
int data;
protected:
int fooImpl()
{
cout << "foo" << endl;
return data;
}
public:
int foo()
{
return this ? fooImpl() : 0;
}
};
int main()
{
Sample* p = new Sample();
p->foo();
return 0;
}
// Compile: clang++ -std=c++14 -o 04_C_testCallCheck 04_C_testCallCheck.cpp
foo
However, even though the instance is null, the instance can call a member function. Code 5 show the case.
// 05_C_testNullInstance.cpp
#include <iostream>
using namespace std;
class Sample
{
int data;
public:
void foo() // void foo(Sample* this)
{
cout << "foo" << endl;
}
};
int main()
{
Sample* p = nullptr; // Assume that creating an instance is failed
p->foo(); // foo(p); -> foo(nullptr);
return 0;
}
// Compile: clang++ -std=c++14
// -o 05_C_testNullInstance 05_C_testNullInstance.cpp
and the output of Code 5 is
foo
Therefore, this is not correct way. Since we know how a static member function works, we can use it. Code 6 is recommend way to call and check an instance.
// 06_C_testNullInstancePrevent.cpp
#include <iostream>
using namespace std;
class Sample
{
int data;
void fooImpl() // void foo(Sample* this)
{
cout << "foo" << endl;
return;
}
public:
static void foo(Sample* const p)
{
if (p == nullptr)
{
cout << "Null instance" << endl;
return;
}
return p->fooImpl();
}
};
int main()
{
Sample* p = nullptr; // Assume that creating an instance is failed
Sample::foo(static_cast(p));
// foo(p); -> foo(nullptr);
return 0;
}
// Compile: clang++ -std=c++14
// -o 06_C_testNullInstancePrevent 06_C_testNullInstancePrevent.cpp
Null instance
Function Pointer of Class
A function pointer is a very useful grammar of C/C++. Code 7 shows simple example of a function pointer.
// 07_C_testFunctionPointer.cpp
#include <iostream>
using namespace std;
void foo()
{
cout << "foo" << endl;
return;
}
int main()
{
void (*f1)() = &foo;
f1();
return 0;
}
// Compile: clang++ -std=c++14
// -o 07_C_testFunctionPointer 07_C_testFunctionPointer.cpp
foo
However, a function of a class is slightly different from a normal function. Code 8 shows how to use function pointers of a class.
// 08_C_testMemberFunctionPointer.cpp
#include <iostream>
using namespace std;
class Dialog
{
public:
static void goo()
{
cout << "goo" << endl;
return;
}
void hoo()
{
cout << "hoo" << endl;
return;
}
};
int main()
{
void (*fs)() = &Dialog::goo;
fs();
void (Dialog::*fn)() = &Dialog::hoo;
Dialog dialog;
(dialog.*fn)();
return 0;
}
// Compile: clang++ -std=c++14
// -o 08_C_testMemberFunctionPointer 08_C_testMemberFunctionPointer.cpp
goo
hoo
In Dialog class, there are two member functions, goo() and foo(). A difference between both is whether or not the function is static. A static function can be called without an instance. It is similar to a normal function pointer. One thing different is a class name before a function.
However, a normal member function is complicated. When a function pointer of a normal member function is declared, it requires a name of the class before a function name. Also, it requires a target instance, because an argument of this pointer is implicitly existed in a member function.
Summary
- A member function has implicitly this pointer argument. To define and use a function pointer,
void (Dialog::*fn)() = &Dialog::hoo;
Dialog dialog;
(dialog.*fn)();
// Function pointer for class member functions
- A static member function does not have this pointer argument. Also It can be called without a specific instance. To define and use a function pointer
void (*fs)() = &Dialog::goo;
fs();
// Function pointer for static class member functions
- A member function can be called by null pointer instance. However, it does not have this pointer, so it cannot use any member variables.
COMMENTS