Fun With C++ [Beginners Stay Away, Lest Ye Be Corrupted]

Started by
30 comments, last by SiCrane 14 years, 1 month ago
Have more time off today, and decided I'd test an interesting little idiom out to help with const-correctness. It turns out you can actually do a lot of neat (and admittedly very dangerous) tricks with placement new, up to and including re-instantiating objects using the this pointer. As far as I can tell, there do not appear to be any adverse effects so long as the idiom itself is used correctly; since we're using the this pointer from an already-constructed object we're guaranteed to have proper alignment and size and recreating the object in this fashion doesn't appear to muck up any lifetimes, etc. Code: main.cpp
#include "SomeClass.h"
#include <iostream>

int main()
{
	{
		SomeClass stackClass;
		stackClass.ReInstantiate();
		stackClass.WriteSomeData();
	}

	std::cout << "---" << std::endl;
	SomeClass* heapPtr = new SomeClass();
	heapPtr->ReInstantiate();
	heapPtr->WriteSomeData();
	delete heapPtr;

	int i;
	std::cin >> i;
	return 0;
}
SomeClass.h:
#pragma once

class SomeClass
{
public:
	SomeClass();
	virtual ~SomeClass();

	void				ReInstantiate();

	void				WriteSomeData();

private:
	char* const			dataChunk;
	const unsigned long		moreData;
	const bool			evenMoreData;
};
SomeClass.cpp:
#include "SomeClass.h"
#include <memory>
#include <iostream>

SomeClass::SomeClass() : dataChunk(new char[32]),
						 moreData(0),
						 evenMoreData(true)
{
	std::cout << "SomeClass()" << std::endl;
}

SomeClass::~SomeClass()
{
	std::cout << "~SomeClass()" << std::endl;
	if(dataChunk)
		delete[] dataChunk;
}

void SomeClass::WriteSomeData()
{
	std::cout << "WriteSomeData()" << std::endl;
	for( size_t i = 0; i < 32; i++ )
	{
		dataChunk = '3';
	}
}

void SomeClass::ReInstantiate()
{
	std::cout << "ReInstantiate()" << std::endl;
	//void* thisPtr = this;
	this->~SomeClass();
	new(this) SomeClass;
}
I would appreciate if someone could test this on something other than MSVC9 (VS2008) to see what happens. No guarantees if it's actually even practical or actually works elsewhere, it just seems interesting.
clb: At the end of 2012, the positions of jupiter, saturn, mercury, and deimos are aligned so as to cause a denormalized flush-to-zero bug when computing earth's gravitational force, slinging it to the sun.
Advertisement
I'm pretty sure I once read an article by Herb Sutter in which he discussed this as a bad idea.
Fascinating, but how does it affect const correctness? Anyway, it works on Mingw32 GCC 4.4.0, except for #pragma once.
Quote:Original post by DevFred
I'm pretty sure I once read an article by Herb Sutter in which he discussed this as a bad idea.
You would be referring to GotW #23, which InvalidPointer should have a good read of.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:Original post by iMalc
Quote:Original post by DevFred
I'm pretty sure I once read an article by Herb Sutter in which he discussed this as a bad idea.
You would be referring to GotW #23, which InvalidPointer should have a good read of.

I actually did manage to dig that up before you posted the link (through the somewhat related GotW #28) and it did validate a few reservations/sticky spots I had regarding inheritance. Obviously, if you call this method on a derived object it's going to die; you aren't even instantiating the same object type! There is something of a workaround, however-- by making the method virtual and fixing types in derived classes it does work correctly. While we're on the subject of bad C++ practice, you could probably even make the ReInstantiate function definition a macro, though I'm sure there might be more esoteric methods of automagically updating this.

Again, no guarantees if it's actually even practical or actually works elsewhere, it just seems interesting.
clb: At the end of 2012, the positions of jupiter, saturn, mercury, and deimos are aligned so as to cause a denormalized flush-to-zero bug when computing earth's gravitational force, slinging it to the sun.
Quote:Original post by InvalidPointer
There is something of a workaround, however-- by making the method virtual and fixing types in derived classes it does work correctly.

Except the v-table could be destroyed in the destructor meaning a copy would be needed?

Quote:Original post by Moomin
Quote:Original post by InvalidPointer
There is something of a workaround, however-- by making the method virtual and fixing types in derived classes it does work correctly.

Except the v-table could be destroyed in the destructor meaning a copy would be needed?

I believe that placement new renders that moot, as it just builds a new one anyway. Actually implementing my proposed solution and calling ReInstantiate on a base class pointer seems to work, though again that may just be result of compiler-specific behavior.

EDIT: Never mind, I see what you're getting at now, and no, it wouldn't be an issue. At this point we've already dispatched the recreation method to the right implementation, and we know at compile time what type of class we want to create-- it's 'baked into' the function itself.
clb: At the end of 2012, the positions of jupiter, saturn, mercury, and deimos are aligned so as to cause a denormalized flush-to-zero bug when computing earth's gravitational force, slinging it to the sun.
I still don't see what this has to do with const-correctness.
While it's interesting that it can be done, I agree with Harb Sutter that it shouldn't be done.

Quote:I still don't see what this has to do with const-correctness.

It allows a reasonable copy constructor/assignment operator to be defined for classes with const member variables (and reference member variables).
void SomeClass::ReInstantiate(){	std::cout << "ReInstantiate()" << std::endl;	//void* thisPtr = this;	this->~SomeClass();	new(this) SomeClass;}


I wonder: if the constructor by any chance happens to throw, would it mean that the destructor would be called again during stack unwinding?

This topic is closed to new replies.

Advertisement