【问题标题】:C++ inheritance and arraysC++ 继承和数组
【发布时间】:2014-10-19 21:52:28
【问题描述】:

这里是c++新手

所以我检查了 C++ 中的类是如何工作的,然后我写了这段代码:

class A{
   ...
}
class B: public A{
...
}
int main(void){
  A array[10];
}

c++ 中的继承与 Java 中的继承方式相同吗? 我可以在数组中添加 B 类型的对象吗?如果可以,我该怎么做? 简单地做array[0] = new B(); 是行不通的

附:只是一个后续问题,有人可以向我展示一个具有简单构造函数的类的对象数组的快速示例吗?出于某种原因,我对此有疑问。如果它有点复杂,我会发布另一个问题。提前致谢

【问题讨论】:

  • 你可以使用A* array[10];,然后写array[0] = new B();。您只能转换为基类的引用和派生类的指针。简单地做array[0] = new B(); 不起作用,因为不可能将A* 分配给A
  • Java 使用指针(称为引用)。如果您在 C++ 中使用指针,它的工作方式(大致)相同。
  • @wowofbob 谢谢,我刚刚尝试过,而且我确实能够将它存储在一个数组中,即使我有一个对象 C
  • Java orject 引用并不完全像 C++ 指针和 C++ 引用,恕我直言,最好分别学习每个
  • 最简单最灵活的方式是vector<unique_ptr<A>>

标签: c++ arrays inheritance


【解决方案1】:

在 Java 中,类类型的变量始终是对对象的引用。即

A a = new A();
A a2 = a; // now a and a2 represent the same object
a.x = 12; // now print(a2.x) will also output 12

new A() 将在堆(可以动态分配的内存区域)上创建实际对象,并返回它的内存地址。 aa2 将(内部)实际上只包含该地址。

与包含实际值的整型变量不同,所以:

int a = 1;
int a2 = a;
a = 3;
// now a2 will still be 1

在 C++ 中,对象被放入堆栈,与整数类型相同(除非您使用引用或指针)。所以:

class A {
public:
   int x;
};

A a;
A a2;
a.x = 1;
a2 = a; // actually calls a2.operator=(a) which is implicitly defined, and assigns all data members (here only int x) the values from the other A
a.x = 2;
std::cout << a2.x << std::end; // will output 1

对于数组仍然如此:如果您声明一个数组A as[10],那么在堆栈上分配的一系列 10 个 A 对象也是如此。

继承

如果你有

class A {
public:
    int x;
};
class B : public A {
public:
    int y;
};

A a;
a.x = 1;

B b;
b.x = 2;
b.y = 500;

// and then:
a = b; // now a.x == 2, but a holds no information about the y value

// with arrays it is the same:
A as[2];
as[0] = b; // now as[0].x == 2, --

执行a = b 只会将b 中的值从其超类复制到a

参考文献

A a;
a.x = 1;
A& a_ref = a;

现在a_ref 是对a 的引用 与 Java 不同,引用不能指向另一个对象。 执行a_ref = a2 将与a = a2 具有相同的效果

a.x = 2
std::cout << a_ref.x << std::endl; // now outputs 2

也不同于Java,a 必须存在,只要a_ref 存在,否则a_ref 无效并且访问它会导致程序崩溃。在 Java 中,对象是在堆上分配的,只有在不再存在指向它的引用时才被释放(这称为垃圾回收)。

B b;
A& a_ref2 = b;
// now a_ref2 is a reference to b.
// (aka B has been downcasted)
// to access b.y from a_ref2, you could do:
int the_y = static_cast<B&>(a_ref2).y

不建议使用此(静态向上转换),仅当您确定 a_ref2 指向 B 对象时才有效。否则它会填充段错误/崩溃。

如果A 是多态的(见下文),则可以使用dynamic_cast&lt;B&amp;&gt;(a_ref2) 代替(仍然不推荐)。如果a_ref2 不是B,它会检测到错误并抛出异常。

多态性

class A {
public:
    virtual int get_v() { return 1; }
    int get() { return 1; }
    int get_a() { return 3; }
};
class B : public A {
public:
    int get_v() override { return 2; } // 'override' is not needed, and only works in C++11
    int get() { return 2; }
};

B b;
A& a_ref = b;

b.get_v(); // returns 2
b.get(); // returns 2
b.get_a(); // returns 3, by inheritance

a_ref.get_v(); // returns 2, because get_v is a virtual function.

因为虚函数 A 和 B 是多态类。在编译时不知道这是否会调用A::get_vB::get_v,因为a_ref 的类型是A&amp;。相反,要调用的函数是在运行时决定的,具体取决于 a_ref 指向的内容。 Java中所有的函数都是这样的。

a_ref.get(); // returns 1.

因为get() 不是多态的,它调用Aget() 函数,因为a_refA&amp; 类型。

a_ref.get_a(); // returns 3, by inheritance

指针

指针类似于引用,但级别较低。您访问引用的方式与实际对象相同(a_ref.xb.x)。指针变量是地址。与引用不同,它们可以在初始赋值后指向另一个对象:

B b; // same classes A and B as before
A* a_ptr = &b; // a is now a pointer to b.
// The type A* means "pointer to B". &b means "address of b".

// Polymorphism works the same:
a_ptr->get_v(); // returns 2
a_ptr->get(); // returns 1.
a_ptr->get_a(); // returns 3.

动态内存分配也返回指针:

A* a_ptr = new B(); // a_ptr is now a pointer to a B allocated on the heap
...
delete a_ptr; // must be done to deallocate the B, otherwise there will be a memory leak.

因为 a_ptr 的类型是 A*(而不是 B*),所以多态类的析构函数应该是虚拟的:

class A {
public:
    ...
    virtual ~A() { .... }
};
class B : public A {
public:
    ...
    ~B() override { .... }
};

否则只有A::~A会被调用。

所以在你的情况下,你可以这样做:

A* array[10];
array[0] = new B();

array[0]->get_v();
delete array[0];

但是数组中所有未初始化的指针都是无效的。 IE。 array[1]-&gt;get_v()delete array[1] 将是一个错误。

array 将是一个指针数组,指向 A 类型的对象或A 的子类。

您还可以像这样动态分配 A 数组:

A* array = new[10] A; // array is now a pointer to an array
array[0].x = 1;
delete[] array; // Deallocates as many items as where allocated

但这和以前一样A as[10],只是在堆栈上。这些将是 A objects ,并且执行 array[0] = b 只会从 b 复制 b.x

unique_ptr

一个好的解决方案可能是使用来自 STL 的std::unique_ptr。它是一个指针的包装器,因此没有两个unique_ptr 可以指向同一个项目。 (因为禁止复制unique_ptr等):

#include <memory>
// needed for std::unique_ptr

std::unique_ptr<A> array[10];
array[0].reset( new B() ); // needed to assign a value to it
array[0]->get_v();

按预期工作。 array 中未赋值的元素默认初始化为零。访问它们会引发异常而不是段错误等。

项目不能用= 分配(因为语义不同)。相反,reset 分配了指针。如果它之前有另一个指针,则该指针是 delete'd 第一个。也不需要(或可能)delete array[0]unique_ptr 会在变量 array 超出范围时删除项目。

允许多个指针指向同一个对象的解决方案,这样该对象只有在没有指针指向它时才会被释放,这也在 STL 中:shared_ptr

数组构造函数

对于堆栈上的项目数组,如

A array[10];

它将始终调用默认构造函数(不带参数)。无法向它传递参数。

std::array 可以复制初始化(即构造函数采用单个值:)

class A {
public:
    int x;
    A(int nx) : x(nx) {}
};

std::array<A, 3> ar = { 1, 2, 3 };
// or
std::array<A, 2> ar2 = { a, a2 }; // makes copies

【讨论】:

  • 可靠的答案,虽然有点长
猜你喜欢
  • 1970-01-01
  • 2015-08-23
  • 1970-01-01
  • 2014-01-09
  • 1970-01-01
  • 2015-12-09
  • 1970-01-01
相关资源
最近更新 更多