繁体   English   中英

一个指针,c ++中的两个不同的类

[英]One pointer, two different classes in c++

假设我有两个结构ab ,每个结构中a几个变量(大多数变量是c ++核心类型,但不是全部)。

有没有办法创建一个名为c指针,它可以指向其中任何一个? 或者,有没有办法创建一个可以容纳其中任何一个的集合?

谢谢

创建指向两者中任何一个的指针的常用方法是使它们从公共基类继承。 基类的任何指针都可以指向任何子类。 请注意,这样您只能通过该指针访问属于基类的元素:

class Base {
public:
    int a;
};

class Sub1 : public Base {
public:
    int b;
};

class Sub2 : public Base {
public:
    int c;
};


int main() {
    Base* p = new Sub1;
    p.a = 1; // legal
    p.b = 1; // illegal, cannot access members of sub-class
    p = new Sub2; // can point to any subclass
}

你想要实现的是多态 ,它是面向对象编程的基本概念之一。 访问子类成员的一种方法是向下转换指针。 执行此操作时,您必须确保将其强制转换为正确的类型:

static_cast<Sub1*>(p).b = 1; // legal, p actually points to a Sub1
static_cast<Sub2*>(p).c = 1; // illegal, p actually points to a Sub1

至于你的第二个问题,使用上面描述的技术,你可以创建一组指向基类的指针,然后可以保存任何子类的实例(这些也可以混合使用):

std::set<Base*> base_set;
base_set.insert(new Sub1);
base_set.insert(new Sub2);

或者,有没有办法创建一个可以容纳其中任何一个的集合?

看看Boost.AnyBoost.Variant 如果你只有2个类,那么variant就足够了。 如果你计划其他类型,并且不想重新编译这个'set',那么使用any

然后,使用中的任任何容器anyvariant

#include <boost/any.hpp>
#include <boost/variant.hpp>

#include <vector>

class A { };
class B { };
class C { };

int main()
{
    // any

    std::vector<boost::any> anies;
    anies.push_back(A());
    anies.push_back(B());

    A a0 = boost::any_cast<A>(anies[0]);
    A b0 = boost::any_cast<A>(anies[1]); // throws boost::bad_any_cast

    // variant
    std::vector<boost::variant<A,B> > vars;
    vars.push_back(A());
    vars.push_back(B());

    A a1 = boost::get<A>(vars[0]);
    A b1 = boost::get<A>(vars[1]); // throws boost::bad_get

    // and here is the main difference:
    anies.push_back(C()); // OK
    vars.push_back(C());  // compile error
}

编辑:当然, variant也可以有2个以上的课程。 但是扩展variant使得能够在不重新编译的情况下保持新的未预料到的类型。

如果ab不相关,那么你可以使用void*或者更好的是any类型的boost

如果ab的超类,则可以使用a*代替。

如果他们都从相同的类型继承你可以做到。 这就是OOP框架如何工作,所有类都继承自Object。

抽象类!!!! - 简单的解决方案

具有可用作指向多个派生子类的指针的基类。 (不需要铸造)

当您在其中使用虚拟方法时,将定义抽象类。 然后你在子类中实现这个方法......简单:

// abstract base class
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    virtual int area (void) =0;
};

class Rectangle: public Polygon {
  public:
    int area (void)
      { return (width * height); }
};

class Triangle: public Polygon {
  public:
    int area (void)
      { return (width * height / 2); }
};

int main () {
  Polygon * ppoly1 = new Rectangle (4,5);
  Polygon * ppoly2 = new Triangle (4,5);
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  cout << ppoly1->area() << '\n';
  cout << ppoly2->area() << '\n';
  return 0;
}

只需定义一个公共超类C和C的两个子类A,B。如果A和B没有公共结构(没有公共属性),则可以将C保留为空。

定义:

A *a = new A();
B *b = new B();
C *c;

然后你可以做到这两点

c = a;

要么

c = b;

虽然你可以这样做,那指针意味着什么? 如果你的应用程序的任何部分被指向' ab '的指针,它就不能用它做很多事情,除非你提供额外的类型信息。

提供额外的类型信息将导致客户端代码如

if( p->type == 'a' ) {
   ... a-specific stuff
} else if( p->type == 'b' ) {
   ... b-specific stuff
} ...

哪个不是很有用。

最好将“类型特定性”委托给对象本身,这是面向对象设计的本质,而C ++有一个非常好的类型系统。

class Interface {
 public:
    virtual void doClientStuff() = 0; // 
    virtual ~theInterface(){};
};

class A : public Interface {
    virtual void doClientStuff(){ ... a-specific stuff }
};

class B : public Interface {
    virtual void doClientStuff(){ ... b-specific stuff }
};

然后你的客户端代码将变得更加不知道,因为类型切换是由C ++为你完成的。

void clientCode( Interface* anObject ) {
   anObject->doClientStuff();
}

Interface* i = new A();
Interface* j = new B();

clientCode( i );
clientCOde( j );

做这件事有很多种方法:

  1. 如果存在继承关系,则使用更通用的基类型。
  2. 使用void *并在适当的位置显式转换。
  3. 使用#1所需的继承关系创建包装类。
  4. 通过联合使用区分容器。

由于其他人已经描述了前三个选项,我将描述第四个选项。 基本上,有区别的容器使用union类型来使用单个对象的存储来存储多个不同值中的一个。 通常,这样的并集与枚举或整数类型一起存储在结构中,用于区分当前在联合类型中保存的值。 举个例子:

// Declarations ...
class FirstType;
class SecondType;

union PointerToFirstOrSecond {
   FirstType* firstptr;
   SecondType* secondptr;
};

enum FIRST_OR_SECOND_TYPE {
   FIRST_TYPE,
   SECOND_TYPE
};

struct PointerToFirstOrSecondContainer {
   PointerToFirstOrSecond pointer;
   FIRST_OR_SECOND_TYPE which;
};

// Example usage...

void OperateOnPointer(PointerToFirstOrSecondContainer container) {
    if (container.which == FIRST_TYPE) {
       DoSomethingWith(container.pointer.firstptr);
    } else {
       DoSomethingElseWith(container.pointer.secondptr);
    }
}

请注意,在下面的代码中,“firstptr”和“secondptr”实际上是同一个变量的两个不同视图(即相同的内存位置),因为联合会为其内容共享空间。

请注意,即使这是一个可能的解决方案, 我也不会推荐它 这种事情不是很容易维护。 如果可能的话,我强烈建议使用继承。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM