A method executed on an object before the object has been initialized?

#include <iostream> using namespace std; class Foo { public: Foo(): initialised(0) { cout << "Foo() gets called AFTER test() ?!" << endl; }; Foo test() { cout << "initialised= " << initialised << " ?! - "; cout << "but I expect it to be 0 from the 'initialised(0)' initialiser on Foo()" << endl; cout << "this method test() is clearly working on an uninitialised object ?!" << endl; return Foo(); } ~Foo() {}; private: int initialised; }; int main() { //SURE this is bad coding but it compiles and runs //I want my class to DETECT and THROW an error to prevent this type of coding //in other words how to catch it at run time and throw "not initialised" or something Foo foo=foo.test(); } 
+1
source share
4 answers

You really can't stop people from coding badly . It works the same as it should:

  • Allocate memory for Foo (this is the value of the "this" pointer)
  • Going to Foo :: test by executing: Foo :: test (this), in which
  • It gets the value this-> initialized, which is a random hit, then it
  • Calls the default constructor Foo (due to returning Foo ();), then
  • Call the copy constructor Foo to copy the edit to Foo ().

As it should. You cannot stop people from not knowing how to use C ++ correctly.

The best you could do is have a magic number:

 class A { public: A(void) : _magicFlag(1337) { } void some_method(void) { assert (_magicFlag == 1337); /* make sure the constructor has been called */ } private: unsigned _magicFlag; } 

This "works" because the odds of _magicFlag are distributed where the value is already 1337.

But really do not do this.

+1
source

Yes, it calls a function on an object not yet built, which is undefined. You cannot find it reliable. I would say that you also should not try to detect it. This is nothing that could happen by chance, compared, for example, with a function call on an already remote object. Trying to catch all the possible mistakes is almost impossible. The declared name is already visible in its initializer for other useful purposes. Consider this:

 Type *t = (Type*)malloc(sizeof(*t)); 

This is a common idiom in C programming and that still works in C ++.

Personally, I like this Herb Sutter story about null links (which are also invalid). The bottom line is that do not try to protect against cases when the language is explicitly forbidden and, in particular, in their general case it is impossible to reliably diagnose. Over time, you will receive false protection, which becomes quite dangerous. Instead, learn your understanding of the language interface and design (avoid pointers to raw, ...), which reduces the chance of errors.

In C ++, as well as in C, many cases are not explicitly prohibited, but remain undefined. Partly because some things are quite difficult to diagnose efficiently and partially, because undefined behavior allows alternative development behavior for it, rather than completely ignoring it - which is often used by existing compilers.

In the example above, for example, any implementation can freely throw an exception. There are other situations similar to undefined behavior, which are much more difficult to diagnose for implementation: the presence of an object in another translation system, available before its creation, is such an example, which is known as the fiasco of the static initialization order.

+5
source

The constructor is the method you want (it does not start before initialization, but rather during initialization, but it should be OK). The reason this doesn't work in your case is because there is undefined behavior here.

In particular, you use a non-existent foo object to initialize (for example, foo in foo.Test() does not exist yet). You can solve this by creating an object explicitly:

 Foo foo=Foo().test() 

You cannot check it in the program, but perhaps valgrind can find this type of error (like any other access to uninitialized memory).

+2
source

You get a lot of answers that basically say: "You should not expect the compiler to help you with this." However, I agree with you that the compiler should help with this problem through some kind of diagnostics. Unfortunately (as other answers indicate), the language specification does not help here - as soon as you get into the part of the initializer of the announcement, the new declared identifier is in scope.

While DDJ had an article about a simple debugging class called "DogTag" that could be used as a debugging aid to help with:

  • using an object after deletion
  • overwriting object memory with garbage
  • using an object before initializing it

I did not use it much - but it really got into an embedded project that worked in some cases with rewriting memory.

This is basically a development of the MagicFlag technique described by GMan .

0
source

All Articles