Link to home
Start Free TrialLog in
Avatar of gaohuanzx
gaohuanzx

asked on

pointer to two different class

I wrote one new class for a very big project. There are is already one class in the origional class. For one mode, it need run the original class function, for another mode, it need run the new class function I wrote. These two class have the same functions and called many place. I want to used the same point name as the original one. for one mode, it points to the original class object, for another, point to new one. Can anybody tell me how to do this. btw, the original point name is global.
Avatar of Fallen_Knight
Fallen_Knight

i'm not sure if i understand this,

calss1 {
    func()
}

class 2 {
    func()
}

and you want a pointer that can point at either class1 or class2 and call func?
Avatar of gaohuanzx

ASKER

yes, you are right, do you have the solution?
give a skeletal structure. then its' possible to understand clearly.
rajeev devin: I did not get what you mean, can u give me a more detail, or show me an example?
The Basic Idea is if you know the type of the class
then you can explicitly call a method in a specific class by using it's namespace:

for func() in class1 call:
class1::func()

for func() in class2 call:
class2::func()
the way fallen_knight has given. a simple structure of your design.
ASKER CERTIFIED SOLUTION
Avatar of Fallen_Knight
Fallen_Knight

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Another way:

class BaseClass
{
public:
virtual func() = 0;
};

class class1:
public BaseClass
{
 func(){ do this;};
}

class class2
public BaseClass
{
func(){do that;);
}

.
.
.

BaseClass* pMyObj = ...

use casting:
for class1
((clas1*)pMyObj)->func()

for class2
((clas2*)pMyObj)->func()


cheers.
opps i forgot the virtual in my example (witch is pretty much the  exact same way as locomojo)

class interface {
   virtual void func(void) = 0;
}
rajeev devin: I did not get what you mean, can u give me a more detail, or show me an example?
I believe what you want is a baseclass and virtual functions:

class Base {
public:
   virtual ~Base() { cout << "Base::~Base" << endl; }
   virtual void func() = 0;
   virtual int another_func(int x) = 0;
};

class OneClass : public Base {
public:
   virtual ~OneClass() { cout << "OneClass::~OneClass" << endl; }
   virtual void func() { cout << "OneClass::func" << endl; }
   virtual int another_func(int x)
   {
      cout << "OneClass::another_func(" << x << ")" << endl;
      return x + 1;
   }
};

class AnotherClass : public Base {
public:
   virtual ~AnotherClass() { cout << "AnotherClass::~AnotherClass" << endl; }
   virtual void func() { cout << "AnotherCLass::func" << endl; }
   virtual int another_func(int x)
   {
      cout << "AnotherClass::another_func(" << x << ")" << endl;
      return x - 1;
   }
};

Base * a = new OneClass;
Base * b = new AnotherClass;

int x = 3;
a -> func(); // will print out OneClass::func
x = a -> another_func(x);
  // will print out OneClass::another_func(3)
  // will return 4 and x is set to 4.
b -> func(); // will print out AnotherClass::func
x = b -> another_func(x);
  // will print out AnotherClass::another_func(4)
  // will return 3 and x is set to 3.

delete a;
// will print out OneClass::~OneClass
// and then Base::~Base
delete b;
// will print out AnotherClass::~AnotherClass
// and then Base::~Base

calling the function through the pointers a and b will go to the correct class depending on the actual type. Both a and b are of type pointer to Base but the objects pointed to are of subclasses of Base. Using virtual function mechanism you select which function to call based on the actual type of the object and not the declared type of the pointer.

This is implemented by having each object of type Base or derived subclasses of Base have an extra pointer to a virtual table. This is often referred to as the vtable pointer. This is an extra member in the class which isn't declared by you and is therefore hidden. It has no real name and you can't really refer to it by any particular manner. It is the constructor of the class that initializes this member so when the constructor for AnotherClass is called it will set the vtable pointer after the initialization list is processed but before the body of the constructor is executed. In the case above it appear that OneClass and AnotherClass doesn't have constructors but they do, C++ will define one constructor for each class because it has a vtable pointer and call that constructor to construct the object. If you write an explicit constructor the vtable pointer will be set by that constructor.

Alf
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Here is the modified version of your code. It will work fine.

class interface {
public:
   virtual void func(void) = 0;
};

class class1 : public interface  {
public:
     void func(void)
     {
        cout << "From class1";
     }
};

class class2 : public interface {
public:
     void func(void)
     {
        cout << "From class2";
     }
};

void main()
{
     interface *ptr;

/* To access the func() of class1 */
     ptr = new class1();
     ptr->func();
     delete ptr;
/* To access the func() of class2 */
     ptr = new class2();
     ptr->func();
     delete ptr;

}

Hope this may help you
Regards
locomojo,

actually, what you just designed IS an interface....

so your "no need for interface" is somewhat out of place :-)

Alf
Reply to Salte:

Interface is a class !!!
with only pure virtual members.

and preferably all public so it will be a struct.
Reply to locomojo:

I dont' think a virtual class is a structure. A structure should poccess data in itself. A virtual function is just an interface to the outside world, or more specifically, class user.
Reply to rajeev_devin:

What I ment was...
Instead of declaring a class with pure virtual methods which mast be public ... just declare it as a struct..

instead of this:
class MyInterface
{
public:
virtual void func1();
vitrual void func2();
);

Do that:

struct MyInterface
{
virtual void func1();
vitrual void func2();
);


thats all :-)
That depends on your definition for interface.

To me an interface is:

a set of function signatures that belongs together and allows you to manipulate a certain functionality. What it does NOT contain is the implementation of those functions - however, this is not absolute. If an interface provide a function A and another function B then you can implement a function DoAthenB which can be implemented such that it physically is a part of the interface but it is provided with a body that does A and then B. However, since it is implemented only in terms of functions which are themselves not defined by the interface it is still a part of the interface even though the function is provided with implementation. You can also take the view that that function is just a convenience function and as such is not part of the interface - i.e. the implementation doesn't have to provide a body for that function since it is already provided.

Win32 is an interface, it's your basic interface to windows.

Posix is another interface, it's your basic interface to posix compatible systems.

A class with only pure virtual functions is an interface, it allows you to manipulate all subclasses that implements that interface. I.e. they implement the body of the functions. However, here too comes the section about convenience functions which are implemented in terms of the pure virtual functions but which are themselves not pure virtual. They are still a part of the interface but they do not require implementation by the implementation since they already have one. However, they require their underlying functions (the pure virtual ones) to be implemented and operate in terms of those.

A class - any class - not just with pure virtual functions - provide an interface. With it you can manipulate all objects of that class. However, unless the interface have virtual functions you cannot manipulate subclasses of that class through that interface.

Of course, you can say "that wasn't the definition of interface I had in mind" but that's a matter of definition. Your usage of the term is a rather restricted form that has its usage in that you create an interface without any default implementation or without any implementation by making a class with only virtual functions:

class MyInterface {
public:
   virtual int do_A() = 0;
   virtual int do_B(....) = 0;
};

Since you have pure virtual functions in it, you cannot create any object of that class and so it serves ONLY as an interface. However, this does not mean that a class which CAN be instantiated can NOT serve as an interface, it only means that a class such as MyInterface serves no other purpose BUT to be an interface. Other classes also provide interfaces, in fact EVERY class does.

Also, note that it is enough to have ONE pure virtual function for the class to be a pure interface class. The reason is that you cannot instantiate it as is and MUST provide subclasses that implement the pure virtual function.

A class with ONLY pure virtual functions may also be called an interface but that does not exclude other classes from being interfaces.

Alf
locomojo,

you can define an interface like this:

struct Interface {
   virtual int foo() = 0;
   virtual void bar() = 0;
};

This makes use of the fact that a struct is the same as a class but with public access.

class C : public D {
public:
   decls;
};

is equivalent to:

struct C : D {
   decls;
}

and

struct C : private D {
private:
   decls;
};

is equivalent to

class C : D {
   decls;
};

However, it is a matter of CONVENTION to NOT use struct for classes that contain methods other than convenience methods and specifically no virtual functions or methods. So it is actually common to write:

class Interface {
public:
   .....
};

instead, yes you get that extra word 'public:' there but using 'class' indicate that here comes something that is more than just a dumb record.

You can also have functions/methods in structs but they are just for convenience and you could just as well have declared them outside:

struct foo {
   int a;
   int b;
   int sumab() const { return a + b; }
};

You might as well have declared:

int sumab(const foo * p) { return p -> a + p -> b; }

but putting it in the struct is more convenient.

'virtual' should definitely imply a class and not a struct it is a signal to the reader (the human reader) that this is more than just a dumb record.

For the C++ compiler the terms struct and class means more or less the same but it is a very good convention that you hold them far away from each other and never mix them "just because".

Examples of places where you CAN mix struct and class is typically like this:

struct S { ... };

class C : public S {
  // no data, only methods to operate on S's data.
  // no virtual functions either since that would imply
  // a vtable pointer.
  // also, the constructors if any should only do simple
  // stuff like computing the data to store in S.
  // This is because it is possible to get an object
  // without every constructing it using constructor.
  // specifically there shouldn't be any destructor
  // defined or needed.
};

Essentially you end up with sizeof(C) == sizeof(S) and you can have an S and just cast it to a C and use the functions in there, you can also construct the object as an S< just cast it to C and then use it. The C constructor was never called and the C destructor will not be called. For this reason the C class should not have any destructor.

If you need virtual functions or virtual baseclass or a destructor or anything more fancy than above. Then you do it like this:

class C { // no inheritance.
private:
   S s;
public:
   ....regular class...
   C(const S & ss) : s(ss) {}

   ...
   operator const S & () const { return s; }
   const S & operator -> () const { return s; }
};

This is a regular class and can have inheritance but the struct S is not provided through inheritance or if it is it must be inherited as private or protected but it is best if it isn't inherited at all (should be no need to inherit it since it is a struct and doesn't have any virtual functions).

You can even overload the operator -> so the C can reach the s members (read only) as:

C c;

c -> a; etc..

Or by casting.

The point is that you can mix with struct as baseclass and a subclass being a class that provide functions for that struct - including constructors but there are many limitations to it and if you want anything beyond those limitations don't do it that way, put the struct inside the class instead. Then you can do whatever you want without restrictions.

So yes, as far as the language is concerned the two words means almost the same thing but they do mean different things to programmers and they should mean different things to programmers. Doing tricky stuff like:

struct C { // This is really a class!
private:
   int k;
public:
   virtual int foo();
   ...
};

is possible but none-the-less bad style. This is true even if you have only public pure virtual functions as members as you sometimes would in your typical interface class.

Alf
Salte:

You are right for most things...
But when you declare an Interface ...
You expect that any class that implement that interface
and referenced by the interface pointer...
the method that would be called is the one implemented in the class implements that interface...

I know that declaring and interface with some basic implementation of methods is common but it is a bad O-O desighn.

Interface should not implement anything instead of declarations.
If something is "good" or "bad" O-O design is very much a matter of opinion. In my experience something is good design if it works well for its purpose and is even better design if you can easily extend it beyond its original purpose.

If you have an interface that define basic functions like this:


class Interface {
public:
   virtual void do_A(int x) = 0;
   virtual void do_B(int x) = 0;
};

If 99% of the usages then becomes something like:


void do_something(Interface * if, int x)
{
   if -> do_A(x);
   if -> do_B(x);
}

Then the interface specification might be extended to also include a new function:

class Interface { // version 2
public:
   virtual void do_A(int x) = 0;
   virtual void do_B(int x) = 0;
   void do_A_then_B(int x)
   {
      do_A(x);
      do_B(x);
   }
};

In your mind it is no longer a pure interface and it is "bad" design, but really what is bad with it? It is a pure extension, people who want to do A but not B can still do so, it just cover that vast majority who do A and then B and help them do it right without getting an error in that the variable x shich is supposed to be the same for the two functions changes value in the meantime.

If the previous interface was "good" then how can this be "worse". I would say it is "better" in that it is a supsrset of the previous and also contain some convenience function that cover what the majority of users need.

Now you might even encounter that a large portion of the implementations of do_B() is done by calling do_A() with a value of (x >> 1) twice. There are some interfaces that implement do_B differently but there are enough of them that we might even provide a reasonable default:

class Interface { // version 3
public:
   virtual void do_A(int x) = 0;

   virtual void do_B(int x)
   {
      do_A(x >> 1);
      do_A(x >> 1);
   }

   void do_A_then_B(int x)
   {
      do_A(x);
      do_B(x);
   }
};

Now, the majority of interfaces that did implement do_A as above can now remove their implementation since the default is sufficient. Those that need another will override it.

Again, if the original interface is "good" how can this interface be "bad"? All we did was to provide a common default for one of the functions and so the people implementing the interface now has an easier job in that they only need to provide do_A() if this default satisfies their needs.

We eased the burden for implementors and we previously eased the burden for users. When did "ease the burden" imply "bad design"? Who is this design for anyway if it is not for the users and implementors?

I think you should rethink your definitions of "good O-O design" and "bad O-O design" and which is which. Stating that all interfaces with only pure virtual functions are good and all that contain one or more non-pure virtual functions are "bad" is simply too simplistic. After all "good" or "bad" design isn't a matter of counting the number of pure virtual functions or lack of such in an interface.

Alf
Salte:

You are right for most things...
But when you declare an Interface ...
You expect that any class that implement that interface
and referenced by the interface pointer...
the method that would be called is the one implemented in the class implements that interface...

I know that declaring and interface with some basic implementation of methods is common but it is a bad O-O desighn.

Interface should not implement anything instead of declarations.
There's also a solution not using inheritance if you want. This problem though is the archetypical inheritance problem and so that is the most immediate and simple solution.

The other solution is to use templates:

let's say you have two classes:

class A1 {
private:
   ....
public:
   ....
   void func(int x);
   void func2(A1 * p, double x);
   ...
};

and

class A2 {
private:
   ....
public:
   ....
   void func(int x);
   void func2x(A2 * p, int x, int y);
};

Now, the func2 isn't exactly the same types since they have different argument types but that is of no problem for us. Virtual mechanism cannot handle that but using templates we can.

Define a class like this:

template <class T>
class An_traits;

// specialize on A1 and A2
template <>
class An_traits<A1> {
public:
   typedef double func2_arg2_type;
   static void func(A1 * p, int x)
   { p -> func(x); }

   static void func2(A1 * p, A1 * q, double x, int /* y */)
   { p -> func2(q,x); }
};

template <>
class An_traits<A2> {
public:
   typedef int func2_arg2_type;
   static void func(A2 * p, int x)
   { p -> func(x); }

   static void func2(A2 * p, A2 * q, int x, int y)
   { p -> func2x(q,x,y); }
};

Note that the functions in the original classes didn't even have to have the same name or a similar number of arguments.

Write a template function or class that can work on either class:

template <class A, class _traits = An_traits<A> >
class Ax {
public:
  typedef typename _traits traits_type;
  typedef typename traits_type::func2_arg_type f2_type;

  static void do_something(A & a)
  {
    f2_type v = f2_type();
    int y = 3;
    traits_type::func(& a,y);
    traits_type::func(& a, & a,v,y);
  }
};

If you have two classes:

A1 a1;
A2 a2;

Ax<A1>::do_something(& a1);
Ax<A2>::do_something(& a2);

will then call the corresponding functions from both A1 and A2.

Note that the traits types only have typedefs and static functions (enum values are also ok). It can even have static variables but no non-static stuff. You never actually instantiate the classes.

The traits types contain typedefs for the types that differ between the two functions. Also note that A1::func2() had only two arguments and so the third argument of the traits type function is ignored in this case. It is still defined so that its interface fit with the traits type for A2.

Btw, this is ALSO an interface, i.e. the traits types define some interface that allow you to handle the two classes in an identical manner and which allow you to define another template class (another interface) that allow you to manipulate either A1 or A2 through that interface. The Ax class is also not instantiated as done here since it too is only an interface to the real classes A1 and A2 and so only provide typedefs and static functions.

Alf
Reply to locomojo:

Actually I like to go with the convension, not the compiler support. Its' just my opinion. Every programmer may have his likings and dislikings.

Regards
Rajeev
Well Good/Bad/Worse/Better is not issue here...
The point is if you look at the original question you will se that there was a solution which needed an extention..
so when you implement and interface it is a good practice to make it only declerative so you can extent it any time
even if you do not have the sources, oly libs or dlls.
So the so called interface will not over extend funtionality which was not intended... I mean
if a method declared in interface it should be pure virtual.
locomojo,

There you go again:

"I know that declaring and interface with some basic implementation of methods is common but it is a bad O-O desighn."

Why on earth is it bad? I showed an argument as to why it's not bad so unless you explain why it is bad design I simply don't buy your argument.

What is "bad" and what is "good" O-O design? Appearantly to you it means something like:

If a design is exactly the way I think a "good" O-O design should be then the design is good, otherwise it is bad. I.e. you have some form of template as to what is "good O-O design" and if the design fall into that template it is "good" otherwise it isn't.

That may be your idea of good or bad design but the problem is twofold:

1. It is possible to make a design completely according to your specification and in your eyes it is "good" however, it may be a hopeless design that most people only feel frustration from using and every user of it would consider the design to be hopelessly bad. It is not difficult to make such a design, believe me, it's very easy!

2. It is also possible to make a design that isn't according to your idea of a "good O-O design" but the design work and people find it useful, extensible, just enough flexible to fill their needs but not so overly flexible that they loose sight. Such an interface is harder to make than the first but it isn't any more difficult than to write one that follow your "good O-O design". However, you would consider it "bad" but every user of it would consider it a "good" design.

Third, what is this idea about "good O-O design"? I know about "good" design and "bad" design and I know very well what object oriented programming and object oriented design is, but I really don't know what is "good O-O design". A bad design cannot be made good by making it object oriented and a good non-object oriented design wouldn't become bad if you made it object oriented. Good or bad design has - on the whole - very little to do with object orientation.

Also, it is possible to make a good O-O design also in languages like C where you don't even have pure virtual functions so how an interface can be a bad design just because it contain non-pure virtual functions is beyond me. For example the FILE * interface for C style FILE i/o is object oriented and has been used for such a long time that I guess you can safely say it is a good interface - it wouldn't be so popular if it was a bad design. However, you don't find a single pure virtual function in it.

Alf
Well Good/Bad/Worse/Better is not issue here...
The point is if you look at the original question you will se that there was a solution which needed an extention..
so when you implement and interface it is a good practice to make it only declerative so you can extent it any time
even if you do not have the sources, oly libs or dlls.
So the so called interface will not over extend funtionality which was not intended... I mean
if a method declared in interface it should be pure virtual.
Locomojo,

I agree as far as the original question was, that good/bad/worse/better is not the issue and so we can let it lie. It is an interesting debate in itself though but I doubt this is the correct forum for such a debate.

I also believe the original question has been answered. The immediate and most obvious answer is to use inheritance. Another - perhaps not so obvius - solution is to use templates. Templates are better in that they do not require the functions to have the same parameters or type. Templates work on 'concepts', as long as the classes are the same on a conceptual level and you have two member functions (or non-member functions) that operate on the two classes in a conceptually identical manner, i.e. they "do the same job for their respective classes" then you can use templates.

For example if you say that type A and type B are both comparable and this function x is to work on any comparable type T, then you can make a template function:

template <class T> class comparer;

template <class T, class Comp = comparer<T> >
T * find(T a[], int n, const T & e)
{
   int L = 0;
   int H = n;
   while (L < H) {
      int M = (L + H) >> 1; // M is middle point.
      int x = Comp::compare(a[M],e);
      if (x == 0)
         return & a[M]; // found it!
      if (x < 0) // a[M] < e, set L = M + 1.
         L = M + 1;
      else // a[M] > e, set H = M - 1.
         H = M - 1;
  }
  return 0; // not found.
}

This function does binary search on a sorted array of ANY type, the only thing we require of the type is that there is a companion type that have a static function to compare elements of the type.

template <>
class comparer<const char *> {
public:
   static int compare(const char * a, const char * b)
   { return strcmp(a,b); }
};

will be a default comparer for const char * objects so an array of strings can now be searched using find above.

If you want to do a compare without case:

template <>
class no_case_comparer<const char *> {
public:
   static int compare(const char * a, const char * b)
   { return stricmp(a,b); }
};

if you want to do compare on integer values instead:

template <>
class comparer<int> {
public:
   static int compare(int a, int b)
   { return a < b ? -1 : a > b; }
};

if you have a sorted array of strings (sorted while ignoring case) you can call find:

const char ** s = find<const char *,no_case_comparer<const char *>(sorted_table,"hello");

if you have an sorted array of string (not ignoring case)
you can call find:

const char ** s2 = find(another_sorted_table,"world");

if you have an array of int values:

int * p = find(int_table, 3);

etc.

Now, you might even want to consider not having the compare function there, after all most basic types already have a less than etc operator, but you can also provide one for all that aren't otherwise specialized:

template <class T>
class comparer {
public:
   static int compare(const T & a, const T & b)
   { return a < b ? -1 : b < a; }
};

This class assumes that for any two a and b you have:

a < b || a == b || a > b

so if ! (a < b) && ! (a > b) then you must have a == b.

For some types such an assumption isn't valid and then you
cannot use that type for the binary search as written above. The binary search algorithm also assumes the same thing.

Note that this template class for comparer allows you to compare any two elements of any type provided the type implement the less than operator. It doesn't require anything more.

This provides a huge flexibility and allows a user to use the function for many types and if he has a type and he wants to do binary search on it all he has to do is implement the operator < for the type.

Also, since the comparer is an argument to find, the caller can also provide a non-default compare function if he wants to compare the values in a different manner, for example for a sorting function the compare will typically sort the elements in ascending order, but you can create a reverse comparer that sort in descending order:

template <class T, class _comp = comparer<T> >
class reverse_comparer {
public:
   static int compare(const T & a, const T & b)
   { return _comp::compare(b,a); }
};

Now using reverse_comparer as argument to the sort algorithm will sort the elements in descending order. The only thing we require is that 1. the sort function takes a comparer argument as second template argument and 2. There is a comparer that would sort the array in ascending order.

sort<int, reverse_comparer<int> >(table,num_elems);

will then sort an integer array in descending order.

Note that used this way the comparer is also an interface, it contain a function compare() that takes two elements of type T and return a value > 0 if a > b, a negative int value if a < b and the value 0 if a == b. Each specialization of that template is an implementation of that interface.

Alf
No comment has been added lately, so it's time to clean up this TA. I will
leave a recommendation in the Cleanup topic area that this question is:

Answered: Points split between Fallen_Knight and locomojo

Please leave any comments here within the next seven days.

Experts: Silence means you don't care. Grading recommendations are made in light
of the posted grading guidlines (https://www.experts-exchange.com/help.jsp#hi73).

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

-bcl (bcladd)
EE Cleanup Volunteer