summaryrefslogtreecommitdiff
path: root/documentation/constructors-and-destructors.html
blob: e98905cedf8cd1c8caae993b30811b8ee7087dfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
<h1>Constructors and destructors</h1>
<p>
    There is a small but very important difference between constructor and 
    destructors in C++, and the __construct() and __destruct() methods in PHP.
</p>
<p>
    A C++ constructor is called on an object that is <i>being</i> initialized,
    but that is <i>not</i> in an initialized state <i>yet</i>. You can experience this by
    calling a pure virtual method from a constructor. This will make your 
    program crash, even when the pure virtual method was implemented in the
    derived class. The reason for this is that inside the C++ constructor the 
    object is not yet fully initialized, and the object is not yet aware of 
    it's position in the class hierarchy. The call to the pure virtual method 
    can thus not be passed on to the derived object.
</p>
<p>
<pre class="language-c++"><code>
#include &lt;iostream&gt;

// define a base class with a pure virtual method that is called from the
// constructor
class BASE 
{
public:
    // constructor
    BASE() 
    {
        // call the pure virtual method
        doSomething();
    }
    
    // define method that should be implemented by derived classes
    virtual void doSomething() = 0;
};

// define a derived class
class DERIVED : public BASE 
{
public:
    // implementation of the virtual function
    virtual void doSomething() override 
    {
        std::cout &lt;&lt; "doSomething()" &lt;&lt; std::endl;
    }
};

// main procedure
int main()
{
    DERIVED d;
    return 0;
}
</code></pre>
</p>
<p>
    The above program crashes (some compilers even refuse to compile this). 
    Unlike similar code in PHP. In PHP however, when the __construct() method 
    gets called, the object is already fully initialized and it is perfectly 
    legal to make calls to abstract methods that are implemented in derived 
    classes, as you can see in the following example.
</p>
<p>
<pre class="language-php"><code>
&lt;?php

// base class in PHP, in which the an abstract method is called
abstract class BASE 
{
    // constructor
    public function __construct() 
    {
        // call abstract method
        $this->doSomething();
    }
    
    // abstract method to be implemented by derived classes
    public abstract function doSomething();
}

// the derived class
class DERIVED extends BASE 
{
    // implement the abstract method
    public function doSomething() 
    {
        echo("doSomething()\n");
    }
}

// create an instance of the derived class
$d = new DERIVED();
?&gt;    
</code></pre>
</p>
<p>
    This PHP script correctly outputs 'doSomething()'. This happens because the 
    __construct() method in PHP is not a real constructor. It constructs nothing, it 
    has access to all members (that already are constructed), and (when available) 
    also the base class and overridden methods. In fact, __construct() is a 
    normal method that just happens to be the very first method that is called right 
    after the object was constructed, and that is called automatically.
</p>
<p>
    This difference is important for you as a C++ programmer, because you should
    never confuse your C++ constructor with the __construct() method. In the real
    constructor, the C++ object is being constructed while the
    PHP object does not yet exist. After the constructor is finished, the PHP
    object is created, and the __construct() method gets called. It is therefore
    valid to have both a C++ constructor and a __construct() method in your class.
</p>
<p>
<pre class="language-c++"><code>
#include &lt;phpcpp.h&gt;

// actual class implementation
class Counter : public Php::Base
{
private:
    int _value = 0;

public:
    // c++ constructor
    Counter() {}
    
    // c++ destructor
    virtual ~Counter() {}
    
    // php "constructor"
    void __construct(Php::Parameters &params)
    {
        // copy first parameter (if available)
        if (params.size() > 0) _value = params[0];
    }

    // functions to increment and decrement
    Php::Value increment() { return ++_value; }
    Php::Value decrement() { return --_value; }
    Php::Value value() const { return _value; }
};

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows which methods are accessible
        Php::Class&lt;Counter&gt; counter("Counter");
        counter.method("__construct", &Counter::__construct);
        counter.method("increment", &Counter::increment);
        counter.method("decrement", &Counter::decrement);
        counter.method("value", &Counter::value);
        
        // add the class to the extension
        myExtension.add(std::move(counter));
        
        // return the extension
        return myExtension;
    }
}
</code></pre>
</p>
<p>
    The code above shows that __construct() is registered as if it was 
    a regular method - and that's what it is. The counter example that we've 
    used before is now extended so that it is possible to give it an initial
    value.
</p>
<p>
<pre class="language-php"><code>
&lt;?php
$counter = new Counter(10);
$counter-&gt;increment();
echo($counter."\n");
?&gt;
</code></pre>
</p>