在 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() 将在堆(可以动态分配的内存区域)上创建实际对象,并返回它的内存地址。 a 和 a2 将(内部)实际上只包含该地址。
与包含实际值的整型变量不同,所以:
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<B&>(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_v 或B::get_v,因为a_ref 的类型是A&。相反,要调用的函数是在运行时决定的,具体取决于 a_ref 指向的内容。 Java中所有的函数都是这样的。
a_ref.get(); // returns 1.
因为get() 不是多态的,它调用A 的get() 函数,因为a_ref 是A& 类型。
a_ref.get_a(); // returns 3, by inheritance
指针
指针类似于引用,但级别较低。您访问引用的方式与实际对象相同(a_ref.x 和 b.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]->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