Click to See Complete Forum and Search --> : new and polymorphism


Elrond
August 26th, 2002, 11:26 AM
Hi,

I am doing too much coding with MFC, thus I have kind of forgotten some real object coding with C++. I have a little question about polymorphism with the new operator:

Say I have these three classes:

class A {...}; //this is a pure virtual class
class A1: public A {...};
class A2 : public A {...};


I want to do something like this:

BOOL my_function(A param)
{
//A* m_pA
m_pA = new ???;
*m_pA = param;
}


I don't know before hand what derived type of A the parameter will be, but I must create a new object of this type. The definitions of the classes have no real importance. The only solution I have so far is to have an attribute in A that gives the sub type (initialized in the Ctor, cannot be modified). Then I check on this type to make the proper new:

switch( param.type)
{
case type_1: m_pA = new A1; break;
case type_2: m_pA = new A2; break;
}


What's the "correct or best" "OO or C++" solution for that?

I would have another solution using a static function as an object factory...

Paul McKenzie
August 26th, 2002, 11:42 AM
Have a virtual "clone" member function in the base class.

class A {
public:
virtual A* clone() = 0;
};

//this is a pure virtual class
class A1: public A
{
public:
A* clone() { return new A1; }
};

class A2 : public A
{
public:
A* clone() { return new A2; }
};


BOOL my_function(A param)
{
A* m_pA;
m_pA = param.clone(); // calls the right clone function
}

Regards,

Paul McKenzie

Elrond
August 26th, 2002, 11:54 AM
That's a step further than the object factory I was thinking about. Using real polymorphism as it should be. :):)

Thanks a lot Paul.

jflegert
August 26th, 2002, 12:48 PM
Is there a typo in there? Shouldn't you be passing a reference/pointer into my_function?



Thanks,
John

jfaust
August 26th, 2002, 01:11 PM
Preferrable, but not necessary. const reference would be better, and also make clone() a const method. However, everything should work as stated.

Also, you may want to invoke the copy constructor instead of the regular constructor, as in

...
return new A1(*this);
...

etc.

Jeff

jflegert
August 26th, 2002, 01:16 PM
Are you sure? I would think the compiler would complain about trying to instantiate a class that has a pure virtual function.

Reagrds,
John

AnthonyMai
August 26th, 2002, 07:36 PM
Are you sure? I would think the compiler would complain about trying to instantiate a class that has a pure virtual function.


You are right. But please forgive Paul. He is known to frequently post typo's in his sample code. As I says before, I NEVER premise that Paul does NOT know it. I only premise that Paul keep forgetting, even though he KNOWS it.

To any one asking, parameter to
my_function()
should NOT be a pass-by-value parameter of class A, since you can never instantiate A which contains a pure virtual function.

jfaust
August 26th, 2002, 07:48 PM
Are you sure? I would think the compiler would complain about trying to instantiate a class that has a pure virtual function.


Umm, oh yeah... <removes foot from mouth>

Jeff

Paul McKenzie
August 26th, 2002, 08:58 PM
Originally posted by AnthonyMai


You are right. But please forgive Paul. He is known to frequently post typo's in his sample code. As I says before, I NEVER premise that Paul does NOT know it. I only premise that Paul keep forgetting, even though he KNOWS it.

To any one asking, parameter to
my_function()
should NOT be a pass-by-value parameter of class A, since you can never instantiate A which contains a pure virtual function. All this coming from someone who is factually wrong most of the time concerning C++. I didn't compile the code -- I took the function as-is from the original message. Anyway, the main point is that an answer was made, which is far more than what you did in this thread.

You use the term "frequently", but it is very infrequent. Exactly what is your point with the above anyway? You never attempted to answer the original question -- why add the editorial comments (regardless of how untrue they are)?

You must still be hurting that QSort() was a failure. Go take your childish vendetta somewhere else.

Regards,

Paul McKenzie

Elrond
August 27th, 2002, 03:26 AM
Originally posted by Paul McKenzie
All this coming from someone who is factually wrong most of the time concerning C++. I didn't compile the code -- I took the function as-is from the original message. Anyway, the main point is that an answer was made, which is far more than what you did in this thread.

You use the term "frequently", but it is very infrequent. Exactly what is your point with the above anyway? You never attempted to answer the original question -- why add the editorial comments (regardless of how untrue they are)?

You must still be hurting that QSort() was a failure. Go take your childish vendetta somewhere else.


Would it end the argument if I say that it is my mistake? Paul just made a copy of my function in the original post for his answer. :o

In my original code I pass a const reference to pure virtual object but when I wrote the question, I did it without paying attention to it. It was just the principle of getting an object of unknown sub type that I was expecting, and I got an answer for that, thanks to Paul. :)

Anthony, I'd rather have you tell me if Paul answer is incorrect than fighting about who makes typo and who is usually wrong or not... :)

jflegert
August 27th, 2002, 06:45 AM
AnthonyMai,

I find your reply very amusing. This coming from the guy who posted:
Funny that There's never been an maintainability issue with my code. I just write once, get it work frawlessly, and my code are usually left un-touched for months or years. Because it just works and there is no need for any bug fix, nor there is any for for performance improvement.
and then posts some buggy qsort code.

Regards,
John Flegert

AnthonyMai
August 27th, 2002, 03:11 PM
Now there are people who is starting to spreading rumors that my Qsort() does not work.

The fact is it works on all combinations of compilers and systems I tested it on. And no one reported a different result on those systems and compilers. The only thing that I see so far is a suspected problem in a rare case where both virtual inheritance and Portland compiler was used. I said suspected because this was reported by one user and is never verified or confirmed by some one else.

So first there is a doubt whether the claim was true or false, or maybe it is simply something made up by some one. Second, assume it is ture, it could be a bug on Portland compiler itself. Without some one getting deep into it and see what's going on, no body can say for certainty exactly what is at fault.

Since it is an isolated result only on Portland compiler, and only when virtual inheritance is used, I do not consider that a problem with my Qsort() function.

jflegert
August 27th, 2002, 03:21 PM
Hello Anthony,

If you are refering to my reply, I never said your qsort didn't work. You posted the following:
The corrected version of Qsort

Looks like I was copying the original Qsort from some where which contains a bug. But even that doesn't explain why it doesn't work on Paul's machine. Because I compiled the same thing and it works, for both debug and release.

...

Regards,
John Flegert

Philip Nicoletti
August 27th, 2002, 09:30 PM
Previously posted by AnthonyMai

The only thing that I see so far is a suspected problem
in a rare case where both virtual inheritance and Portland
compiler was used. I said suspected because this was reported
by one user and is never verified or confirmed by some one else.

So first there is a doubt whether the claim was true or false,
or maybe it is simply something made up by some one.


Before accusing me of "making something up", please verify the
accuracy yourself. It is very easy to do. Just purchase the
compiler and test it yourself. You will see my statements
were 100 percent accurate and true. That would be the professional,
(not to mention ethical) thing to do.


In the "People who live in glass houses" department :


Previously posted by Anthony Mai (this is the EXACT and COMPLETE post)

Paul asked: "try to memcpy a std::string into another. See what you'll get".

I tried already. It is your turn to try it, Paul. You will get exactly the
same result just as if you do a


std::string str1, str2;
str2 = "Just try it";
str2 = str1;
// Had you done memcpy(&str2, &str1, sizeof(str2));
// It will be the same.


I know this is a C++ forum. But whenever you say something doesn't work
in certain ways in C, and I know you are wrong. I have to stand out and
point out the obvious.

When you say you can't memcpy std::string, I have to show you that
you CAN, without an error. That does NOT mean I promote such usage.


As I showed later in that thread, the memcpy() does NOT produce
"exactly the same result", and was obviously not tested as
claimed by the poster.


For those who still have the Qsort() code and the testing routines
from the previous threads, try sorting 1 million doubles using the
following initialization with both std:sort() and Qsort().


typedef vector<double> V;
typedef vector<double>::iterator IT;

void randomise_vector(V& v)
{
double factor = 3.14159;
if (v.size() > 0) factor /= v.size();
int c = 0;

for (IT i = v.begin(); i != v.end(); ++i)
{
*i = sin(c*factor);
c++;
}
}


And don't bother with another correction to Qsort().
Shouldn't I get paid for finding these obvious problems
with Qsort() ?

AnthonyMai
August 28th, 2002, 01:26 AM
Philip:
Do NOT try to dispute the fact that memcpy() on a std::string produce the same result as an ssignment operator!!! If you do, I really have a reason to believe that you indeed made up the Portland Compiler story. I am not going buy a copy of useless compiler just to argue with you. But here is a piece of code that any one can test in just half a minute:

#include <iostream>
#include <string>

using namespace std;

int main(int argc, char argv[])
{
string str1, str2;
string str3 = "Hello world";

memcpy(&str1, &str3, sizeof(str1));

str2 = str3;

cout << "str1 is: " << str1 << endl;
cout << "str2 is: " << str2 << endl;
cout << "str3 is: " << str3 << endl;

if (memcmp(&str1, &str2, sizeof(str1)) == 0)
{
cout << "The result of memcpy() is the same as assignment" << endl;
}
else
{
cout << "The result of memcpy() is NOT the same as assignment" << endl;
}

return 0;
}


The output is exactly identical. Any one can verify that.

As for Qsort, I already said that in the case of a few extremely simple cases, like just a double or just an int, The simple POD cases, std::sort will be marginally bettern than Qsort, thank to compiler optimization. HOWEVER, in the case where slightly complicated objects are involved, like when it is a case with a couple of strings or vectors, Qsort will beat std::sort() big big time.

AnthonyMai
August 28th, 2002, 01:30 AM
Philip:
Do NOT try to dispute the fact that memcpy() on a std::string produce the same result as an ssignment operator!!! If you do, I really have a reason to believe that you indeed made up the Portland Compiler story. I am not going buy a copy of useless compiler just to argue with you. But here is a piece of code that any one can test in just half a minute:

#include <iostream>
#include <string>

using namespace std;

int main(int argc, char argv[])
{
string str1, str2;
string str3 = "Hello world";

memcpy(&str1, &str3, sizeof(str1));

str2 = str3;

cout << "str1 is: " << str1 << endl;
cout << "str2 is: " << str2 << endl;
cout << "str3 is: " << str3 << endl;

if (memcmp(&str1, &str2, sizeof(str1)) == 0)
{
cout << "The result of memcpy() is the same as assignment" << endl;
}
else
{
cout << "The result of memcpy() is NOT the same as assignment" << endl;
}

return 0;
}


The output is exactly identical. Any one can verify that.

As for Qsort, I already said that in the case of a few extremely simple cases, like just a double or just an int, The simple POD cases, std::sort will be marginally bettern than Qsort, thank to compiler optimization. HOWEVER, in the case where slightly complicated objects are involved, like when it is a case with a couple of strings or vectors, Qsort will beat std::sort() big big time.

Some famous C++ expert published test data that shows std::sort better than qsort(). However they only showed result of simple types like a double or an int. They have NEVER shown result of a none-trivial class containing quite a few none-POD class members. I have shown not only qsort works for none POD data, it works much faster than std::sort.

Paul McKenzie
August 28th, 2002, 04:28 AM
The output is exactly identical. Any one can verify that.And anyone who writes trash like that in production code should be hung out to dry. Memcpy() is not the same as assignment. Assignment invokes op=, memcpy() doesn't. Reference counted strings, anyone?I'm not going buy a copy of useless compiler just to argue with youWhy not? Afraid to find out that your code isn't so great? You made the claim, it is you who should investigate why something doesn't work, not Phillip. In reality, there was no need for investigation -- your code was not guaranteed to work, as defined by ANSI. So why should Phillp waste his time?

So any compiler that doesn't run your "great code" is now "useless". More arrogance.

They have NEVER shown result of a none-trivial class containing quite a few none-POD class members. I have shown not only qsort works for none POD data, it works much faster than std::sort.qsort is not guaranteed to work for non-POD types for all compilers. That is an irrefutable fact, not an opinion.

All you got was undefined behavior that acted in your favor for a certain set of compilers and not even guaranteed to work for every compiler, and not even for compiler versions. This was pointed out by many, but you didn't and won't listen. You are even arrogant enough to put blame on the Portland compiler instead of your own "great code" for the failure. The standard sort works on every compiler, and is guaranteed to work. Your QSort is not guaranteed to work -- plain and simple. Instead of figuring out why QSort and qsort won't work on the Portland compiler for the non-POD type, why don't you examine the reasons std::sort worked correctly for this compiler?

The experts didn't comment or show qsort working for non-POD types because they know that qsort cannot be used on non-POD types without invoking undefined behavior. If it were so easy, why didn't they do it? Are they "all wrong"?

Regards,

Paul McKenzie

Philip Nicoletti
August 28th, 2002, 07:42 AM
Code previously posted by AnthonyMai ...


#include <iostream>
#include <string>

using namespace std;

int main(int argc, char argv[])
{
string str1, str2;
string str3 = "Hello world";

memcpy(&str1, &str3, sizeof(str1));

str2 = str3;

cout << "str1 is: " << str1 << endl;
cout << "str2 is: " << str2 << endl;
cout << "str3 is: " << str3 << endl;

if (memcmp(&str1, &str2, sizeof(str1)) == 0)
{
cout << "The result of memcpy() is the same as assignment" << endl;
}
else
{
cout << "The result of memcpy() is NOT the same as assignment" << endl;
}

return 0;
}



I got the following message :

The result of memcpy() is NOT the same as assignment

on the following systems:

1) pgCC (with a core dump)
2) VC++ version 5 (both debug and release)
3) VC++ version 6 (release)

(Although, it was not my copy of VC++ ver 6, and they did
not install any of the service packs, so I don't really
vouch for the result on that system).


How about this test ? (try both release and debug using VC++)


#include <iostream>
#include <string>

using namespace std;

int main(int argc, char argv[])
{
string str1;
string str3 = "Hello world";

memcpy(&str1, &str3, sizeof(str1));

str3 = "str3 has been modified, but str1 should still be Hello World";

cout << "str1 is: " << str1 << endl;
cout << "str3 is: " << str3 << endl;

return 0;
}

Elrond
August 28th, 2002, 07:54 AM
I have done the same test as Philip with my VC++6 SP2.
#include <iostream>
#include <string>

using namespace std;

int main(int argc, char argv[])
{
string str1, str2;
string str3 = "Hello world";

memcpy(&str1, &str3, sizeof(str1));

str2 = str3;

cout << "str1 is: " << str1 << endl;
cout << "str2 is: " << str2 << endl;
cout << "str3 is: " << str3 << endl;

if (memcmp(&str1, &str2, sizeof(str1)) == 0)
{
cout << "The result of memcpy() is the same as assignment" << endl;
}
else
{
cout << "The result of memcpy() is NOT the same as assignment" << endl;
}

return 0;
}


It works in Debug, but not in Release where I got:
The result of memcpy() is NOT the same as assignment

PaulWendt
August 28th, 2002, 08:55 AM
I, too, have tried the code. The results are not surprising.
On WinNT 4.0 SP6 and VC6 SP5, I get the following in debug:

str1 is: should still be Hello World
str3 is: str3 has been modified, but str1 should still be Hello World
Press any key to continue


In release mode, I get:

str1 is: /
str3 is: str3 has been modified, but str1 should still be Hello World
Press any key to continue


On redhat linux 6.1 with gcc-3.1, I get:

str1 is: Hello world è0H`x£
[Further comment is impossible to give as my terminal became
unresponsive at this point.]


Can you say "undefined behavior" ?

So it's 0/2 on my end. From what I understand, a lot of std::string
implementations use reference-counting to store their strings.
Because of this, memcpy() won't work since reference-counting
usually requires copy constructors to be called when copying an
object [as far as I know anyway].

C++ is not just a superset of C where you can ignore the rules
of the language; you cannot just call qsort and memcpy and
ignore copy constructors all of the time. Sure there are times
where you can, but not ALL of the time.

--Paul

Simon Wilkins
August 28th, 2002, 10:22 AM
Doesn't work when compiled in release mode on NT4.0 SP6 with VC++ 6 SP2. I guess this MUST be a compiler bug, someone really needs to write one that works. It works when compiled in debug so I guess I'll just distribute all my code compiled with debug enabled. That'll fix it.

Seriously,Do NOT try to dispute the fact that memcpy() on a std::string produce the same result as an assignment operatorAnthony take it back, with your knowledge of STL you know it is an incorrect statement, without having to wait for the many posts that are already proving it wrong beyond any doubt. memcpy() on a std::string does NOT produce the same result as an assignment. It is not safe, it is not portable and it does not comply with the C++ standard. The STL and other complex classes employ techniques to improve performance, reduce memory requirements and aid portability that prevent the use of memcpy and other such functions. The only reason they can employ such techniques safely is under the protection of the C++ standard.

I like good debate and have learnt much through following the various threads you have been involved in recently. Mainly thanks to the many people who have spent time investigating your points and concluded that they have severe limitations that make them unusable in all but the most limited of scenarios. Performance is important but not to the extent where it is worth sacrificing maintainability.

Case studies on many large scale projects demonstrate that if software is not designed to be maintainability, the cost of defect rectification and future development increases exponentially with time. This is an uneccessary and unacceptable burden to place on any commercial product and hence any company.

I think in this instance you must conceed, there is no defence what-so-ever for the above mentioned quote.

AnthonyMai
August 28th, 2002, 04:08 PM
Hold on before you guys claim it works for debug version but doesn't work for release version. Think! Think! Think!

As a seasoned professional programmer, you've got to know immediately what it means when something seemingly works for debug version but not for release version. In most case it means you have un-initialized variables. In debug build it puts 0xcccccccc to every un-initialized variable so memcmp would not find any difference. But in release build you are not so lucky.

The size of std::string class is 16 bytes, the last 12 bytes are _Ptr, _Len, _Res, respectively. The first 4 bytes belong to a class that std::string is based on, allocator. allocator is actually an EMPTY class which contains no class data. But because C++ standard does not allow zero size objects, it takes at least 1 byte, the other 3 bytes are padded in for byte alignment.

So you see, the first 4 bytes of the std::string class object is NEVER used. When assignment operator is applied, these 4 bytes are never copied over, causing them to be different, but it is perfectly OK.

Paul does have a valid point in refering to reference counted strings. So, instead of taking my words back, I need to add more words to make it more accurate:

memcpy() on a std::string produces completely equivalent result as an assignment operator, in the context of doing object swapping. And this exact sentence is true even for Portland Compiler.

Simon Wilkins
August 29th, 2002, 03:58 AM
Anthony,

So you see, the first 4 bytes of the std::string class object is NEVER used. When assignment operator is applied, these 4 bytes are never copied over, causing them to be different...Regardless of why your code fails the fact remains it fails. Your explanation of why memcmp reports differences, I believe to be accurate. But this is just another example of the problems encountered using 'C' functions on non 'POD' data. You shouldn't try to use memcmp when comparing object instances as it is not guarenteed to work. You should overload the == operator.
You shouldn't try to use memcpy when making a copy of an object instance as it is not guarenteed to work. You should overload the = operator.
.... but it is perfectly OK.
But its not perfectly OK. If you now try to assign a value to str3 it modifies str1 and the inverse is also true. str1 is NOT a distinct usable copy of str3. The two 'instances' are linked as the string class uses reference counted strings.

In summary, I think were almost there with the wording of your statment, to make sure it is factually correct. I've just added one more small caveat:
memcpy() on a std::string produces completely equivalent result as an assignment operator, in the context of doing object swapping. With the exception that it doesn't work

Whatever 'object swapping' is. I guess it's the difference between creating a distinct and independently usable copy of an instance and creating a copy that is linked to the original instance. Changes in an undefined manner when the original changes and will change the original in undefined manner if it is itself changed.

What more can anyone say, it is quite simple your code:
Is not big
It isn't clever
It's unsafe
It doesn't work in a predictable manner
It violates the C++ standard
It relies on the implementation of the compiler (which could change between versions)
.......


Learn to at least contemplate the prospect that you are wrong. By the time you have finished placing caveat's on your original statement it will be possible to reduce it to the following:

Don't use 'C' functions on non 'POD' data.

The exact opposite of your original statement. You can't buck the standard, in a reliable and predictable manner, no matter how hard you try.

Many posters have alerted you to the danger having no recourse to the compiler vendor if a bug is found. Let me give you a quick example of this:

A project I was involved was using the HP C++ compiler and the STL vector class. Occassionally the compiler would fail to compile the source code. If the order of a few lines of code were changed it would compile for a while and then suddenly stop for no apparent reason. Eventually this was tracked down to be a bug in the compiler due to the implementation of the vector class causing stack corruption during compilation. Had we written code that violated the C++ standard we would never have got HP to admit to this nor had their support in resolving it and expediting a patch to us.

More than enough said.

Philip Nicoletti
August 29th, 2002, 06:17 AM
previously posted by AnthonyMai ...

memcpy() on a std::string produces completely equivalent
result as an assignment operator, in the context of doing
object swapping. And this exact sentence is true even for
Portland Compiler.


It does ??? You tested it for the Portland Group compiler ???

Here is my test on the Portland Group compiler , and output ...



#include <iostream>
#include <string>

using namespace std;

void test_swap();

int main()
{
cout << "before calls to test_swap()" << endl;

test_swap();
test_swap();

cout << "after calls to test_swap()" << endl;

return 0;
}

void test_swap()
{
string str1 = "string 1";
string str2 = "string number 2";
string tmp;

memcpy(&tmp, &str1,sizeof(tmp));
memcpy(&str1,&str2,sizeof(str1));
memcpy(&str2,&tmp, sizeof(str2));
}


The output ...

before calls to test_swap()
Segmentation fault (core dumped)

AnthonyMai
August 29th, 2002, 10:10 AM
Philips:

Your problem is NOT memcpy itself, but you STEPPED ONTO str3 and destroyed it, causing it to crash. Any time you step onto a piece of memory that you should not step on, using whatever method, causing useable data to be flushed away, you are looking for trouble.

Certainly if you stepped onto any object that you should not touch, your code will fail. It has nothing to do with memcpy. When using memcpy, you should NEVER copy to a block of memory that contains useful data that can not be flushed away.

You should memcpy to a block of memory reserved for memcpy:


void test_swap()
{
string str1 = "string 1";
string str2 = "string number 2";
char tmp[64];

memcpy(&tmp, &str1,sizeof(str1));
memcpy(&str1,&str2,sizeof(str1));
memcpy(&str2,&tmp, sizeof(str2));
}