|
I agree with you completely. I did not intend to impose such a limitation. However, I do prefer data to be at a lower level (closer to base) class rather than a derived class at the end of the hierarchy. So as much as a base class provides the interface I also like it to provide data. Of course this can vary depending on the design requirements.
If each derived class has its own data defined then to a large extent we loose the benefits of inheritance. Now one may argue that a certain design requires a base class for interface only and not data and that is fine too.
Now if you look at the example provided in this dicussion we were talking about creating an operator= for Derived A and B. If the contents of B are to be assigned to A then I suppose it was fair on my part to assume that they both contain the same data and therefore I would hope that the data was defined in Base.
|
|
|
|
|
Ranjan Banerji wrote:
If each derived class has its own data defined then to a large extent we loose the benefits of inheritance
It depends. If you have a common set of data which can be used by all derived classes, then by all means place then in base class. However, this is quite rare. Usually your derived classes represent different entities which share part of the implementation (including data members) and add their own stuff.
Take the proverbial example with shape hierarchy. You could place 'color' attribute in the base class (assuming that each shape has exactly one color), but surely you won't add 'center' there, because it would be used by cirle, but not by polygon.
Going back to the operator= example - I think it's easy way to shoot yourself in the foot. If you want to copy you should provide the operator= or other method, otherwise adding new members to derived classes will break the program.
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
Now you are making me respond just for the sake of this discussion .
There is always a design option available for those real life cases, such that data is represented at a lower level, though not necessarily at the abstract base class level.
Taking the shape example one could could have the BaseShape class from which which one could derive single color classes and other color classes etc. From which you derive the various shapes etc.
Now this may be overkill, but there is almost (keyword is almost) always a design solution available.
|
|
|
|
|
I don't see your point. You've started with cast to base class and call to operator= which probably cause slicing. Then you argue that it's viable option, because if you don't store the data in base class then you'll lose the advantages of inheritance.
The approach you propose is IMHO dangerous and applicable in rare case when derived classes differ only in their behavior (do not introduce data members). This rare case can exist for a while in given project, but it's trivial to break it by adding data member(s) to derived class. The program will still compile, but it won't work correctly anymore.
So I can't any advantage of using *a=*b where a and b are cast from derived to base.
Ranjan Banerji wrote:
Taking the shape example one could could have the BaseShape class from which which one could derive single color classes and other color classes etc. From which you derive the various shapes etc
Would you cast to BaseShape and use operator= then?
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
If you use :
base *shape1 = new Shape1
base *shape2 = new Shape2
then you can do *shape1 = *shape2 without casting assuming the data is in base and base has an operator=
|
|
|
|
|
And you've just introduced tight coupling between Shape1 and Shape2. You can't add any data member to Shape1/Shape2 without breaking your program at runtime, since compiler will not barf at you.
Is this something you do frequently in your projects?
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
You are proving to be denser than what I thought. Sorry for my comment but instead of trying to get my point you seem to be focussing on proving me wrong. If thats your attention then let me boost your ego by saying yes you are right. A lot simpler. Learn to smile.
If you learn to design your code KEYWORD is design then you should not be facing the issue of adding data to the Shape1 and Shape2.
That is why you have design prior to coding. Ever heard of that concept?
And yes. If your design requires that Shap1 and 2 have their own data then you do not use the base class operator=. Then one could do what you suggested.
Now I have work to do. So flame away. And have a nice day.
|
|
|
|
|
Ranjan Banerji wrote:
If you learn to design your code KEYWORD is design then you should not be facing the issue of adding data to the Shape1 and Shape2
This is my point, dude. No matter how well you design Shape1 and Shape2 you risk that your program will burn in flames when you use the construct you've proposed (cast/operator= on base), because its enough to change the *implementation* of Shape[1|2]. This breaks the encapsulation; you could use memcpy as well
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
The point you are not getting is that right from the start I have been saying that you are right. I simply said that it is possible to use the base class operator IF you had stable design and if the data was encapsulated at the base level.
You missed out on my IF.
Also, in my experience and this is just my experience it has been rare to do derived1 = derived2. Its like saying if we had a base class of mammals and a derived dog class and derived cat class and then we are saying dog = cat. Which does not exactly make sense. And so once again the design question comes up as to is the class hierarchy correctly designed? If so then why do we need derived1 = derived2.
Now I really need to go and work.
|
|
|
|
|
Hi,
I am creating a framework for an acquisition of (sampled) data, e.g. images at a certain 'sampling' (every 2sec for ex.).
I'd like to use some interfaces to create a kinda 'generic' framework in C++ (therefore by using pure abstract classes):
First, each sample data has basic functionalities, described in an interface:
class ISampleData
{
public:
virtual bool Load(const char* lpszFilenameIn) = 0;
virtual bool Save(const char* lpszFilenameOut) = 0;
...
};
Second, a 'generic' interface for an acquisition digitizer describes basic features:
class IInputDigitizer
{
public:
virtual ISampleData& Acquire() = 0;
...
};
Then, I'd like to create an implementation of these interfaces in the case of image acquisition:
class CImage : public ISampleData
{
public:
CImage();
virtual ~CImage();
virtual bool Load(const char* lpszFilenameIn);
virtual bool Save(const char* lpszFilenameOut);
...
};
class CCameraDigitizer : public IInputDigitizer
{
public:
CCameraDigitizer();
virtual ~CCameraDigitizer();
virtual CImage& Acquire();
...
};
Some problems occur when I try something like this in the main prog:
m_lpDigitizer = new CCameraDigitizer();
CImage image = m_lpDigitizer->Acquire();
with, of course, the error
error C2440: 'initializing' : cannot convert from 'ISampleData' to 'CImage'
I m not sure I understand the problem (reverse polymorhism? pb with abstract class instantiation?)
What can be a cure? a solution? without loosing this kind of design "Generic to Specialized using the elegance of interfaces".
Thanks a lot,
Bertrand
|
|
|
|
|
Bertrand Boichon wrote:
CImage image = m_lpDigitizer->Acquire();
Acquire probably returns ISampleData by value. And you can't copy ISampleData to CImage, because CImage is more complex. You should change the type of 'image' variable to ISampleData *, and return pointer from Acquire.
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
Thanks for your answer Tomasz!
Actually today, while working on the problem, I figured that if I cast it back to "CImage&" it then works!
CImage image = (Cimage&) m_lpDigitizer->Acquire();
But I am not sure I understand why...
I am not very familiar with references and pointers, but in your answer (use of ISampleData*), it seems I would need a dynamic alloc (using new) in the implementation of "Acquire" method, and I will have to destroy the object later (uding delete).
Instead, I'd like to copy the object created in "Acquire", into the object "image" (i.e. "image = m_lpDigitizer->Acquire();").
That is: using the stack, not the heap.
All the best,
bertrand
|
|
|
|
|
Passing/returning by value has disadvantages:
1) there's a copy created with a call to copy constructor. While this isn't a problem for built in types like int or double or small structs like POINT, it may significantly hurt performance for other objects, especially ones which allocate memory in constructor (std::vector, for example).
2) polymorphism may be lost. When you have function like this...
ISampleData Acquire()
{
CImage result;
...
return result;
}
CImage image = Acquire();
... then the 'result' variable doesn't reach 'image'. Instead, temporary object of type ISampleData is created (it may be impossible when ISampleData has pure virtual methods) and copied again into 'image'. Your cast to CImage& is simply cheating, in fact, on the right side of '=' you don't have CImage object anymore.
Bertrand Boichon wrote:
Instead, I'd like to copy the object created in "Acquire", into the object "image" (i.e. "image = m_lpDigitizer->Acquire();").
That is: using the stack, not the heap.
You'd have to return CImage, not ISampleData. As I said above, this may be quite inefficient, however.
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
Ok... more questions:
First of all, what do you mean CImage& is cheating? I mean, to me, it seems logical to use the reference (address in memory) of a CImage object (base on ISampleData interface), since I cannot return an instance (by value) of type ISampleData (pure abstract class).
Secondly, what do you mean by "inefficient"? Passing/returning by value versus by reference?
The other possiblity is to create the object on the heap (with new) and pass/return the pointer by value, like this:
ISampleData* Acquire()
{
CImage* result = new CImage();
...
return result;
}
CImage* image = (CImage*) Acquire();
But then I thought doing that is less "efficient" than copying an object to the 'caller' stack (by returning a reference to a CImage obj)????
The other thing is:
Surprisingly (and I thought the same as you did) polymorphism is NOT lost, and I don't know why...
I made a little experiment: I created a method in my class CImage called Display() which is not overloaded from CSampleData (i.e. just a new method specific to CImage). Then I did exactly as before (with "Acquire()" returning a reference to ISampleData):
CImage image = (CImage&) m_lpDigitizer->Acquire();
And guess what? when I call "image.Display()", well it works fine!
Why? Is it because I am dealing with reference to an object, not the object itself. Polymorphism apply to an object copy (by value), not to a reference copy of it?
Bertrand Boichon
|
|
|
|
|
Bertrand Boichon wrote:
Secondly, what do you mean by "inefficient"? Passing/returning by value versus by reference?
Yes. This is one of C++ basics. Of course, the 'inefficiency' greatly depends on the structure of class. Just remember that each object returned/passed by value is *copied*, and this can be quite expensive.
Could you post the Acquire function; simplify the actual code if needed but leave return type unchanged.
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
Here is the Acquire function, using a reference to a CImage (derived from ISampleData pure abstract class):
ISampleData& CCameraDigitizer::Acquire()
{
CImage sample;
// ... perform actual acquisition here ...
return sample;
}
The question is: what should I return ISampleData& (object by ref) or ISampleData* (pointer to obj, by value)?
I did some more tests on that matter (because I realized I don't know much about passing by reference vs by value). I added a dynamically allocated (on the heap) buffer (for the image itself) and an 'int' data field in the CImage class. Then I tried to use these in methods like Save or Display:
CImage image = (CImage&) m_lpDigitizer->Acquire();
image.Save("TEST.JPG");
image.Display();
I came to realize that even if Save and Display (using the buffer data field) are called correctly (what I saw at first), it actually displays crap since the buffer is pointing a random location, along with the 'int' field.
So the only solution is to return a pointer to a CImage obj (therefore allocated on the heap):
ISampleData* CCameraDigitizer::Acquire()
{
CImage* pSample = new CImage();
// ... perform actual acquisition here ...
return pSample;
}
And it works.
What do you think?
Best regards,
Bertrand Boichon
|
|
|
|
|
Returning the reference to local, non-static object is something you should *never* do in C++. The 'sample' variable is destroyed as soon as function returns - the CImage destructor is called. Your reference points to deallocated stack space which contains undefined bits.
So basically you have two options: allocated new object on the heap and return the pointer (as you did already) -or- pass the CImage object by reference to Acquire:
void CDigitizer::Acquire(CImage & image)
{
}
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
Is there any difference between returning and passing a reference to an object:
void Acquire(ISampleData& image)
vs
ISampleData& Acquire(void)
?
Because I tried the "ISampleData& Acquire(void)", and (of course) it de-allocates data fields when returning CImage reference. (cf previous email).
So you mean actually passing a CImage reference would not do that?
To me, the only option is to pass/return a pointer (to a CImage obj) by value.
bertrand
|
|
|
|
|
Bertrand Boichon wrote:
So you mean actually passing a CImage reference would not do that?
Yes, because you would not declare CImage inside Acquire:
void Acquire(ISampleData & image)
{
image.DoThis();
image.PerformThat();
}
...
CImage image;
Acquire(image);
The image Acquire is operating on is declared outside of function, so it's lifetime isn't bound to Acquire scope.
Returning a pointer to object allocated in Acquire is also OK. You may consider using std::auto_ptr or boost.org scoped_ptr to ensure proper deletion.
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
Thank you very much for the lesson!
Very helpful!
Bertrand Boichon
|
|
|
|
|
To enable / disable the toolbar buttons I use the update handler (ON_UPDATE_COMMAND_UI() macro)... ok... but when does the system change the state (enable/disable) of the toolbar buttons?
Have I to call Invalidate or UpdateWindow ???
--
Nice greets, Daniel.
|
|
|
|
|
'The system' (MFC app) updates the button state during its idle update cycle. See CWinApp::OnIdle and CFrameWnd::OnIdleUpdateCmdUI (undocumented).
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
Hi,
I would like use CMultiRectTracker into CScrollView to manipulate gdi
objects,
such as CRect, but when I try select object after scrolling there is bug
to move and positioning its RectTracker.
Can anyone help me?
Thanks, in advance
Gianfranco
|
|
|
|
|
Without any additional information about 'the bug', I guess you could forgot to convert from device units to logical units (with CDC::DPtoLP) in mouse event handlers.
Tomasz Sowinski -- http://www.shooltz.com
*** Si fractum non sit, noli id reficere. ***
|
|
|
|
|
I can get the select text in html in my ap. But how to get the position of the select text? Because next time I open the same html I want select the text auto.Or have any other way to do it?
|
|
|
|