Java和C#的主要区别在于两者的值类型(value type)和引用类型(reference type)的不同。
- 值类型适用于基本类型,如char、int 和 float,还有C#的structs。区分它们的主要特征在于创建它们时并不需要使用new。还有,在执行赋值运算时会对变量持有者进行复制。例如:
int i = 5;
int j = 10;
i = j;
- 引用类型适用于一些类,比如Integer(在Java中)、String和MyVeryOwnClass。实例是通过new创建的。执行赋值运算符时,只是对指向这个对象的引用的复制。要想获得深度复制(deep copy)的效果,必须调用函数clone() (在Java中)或者Clone() (在C#中)。例如:
Integer i = new Integer(5);
Integer j = new Integer(10);
i = j.clone();
在C++中,所有类型都可以用作“引用类型”,并且那些具有可复制性的类型也还可以用作“值类型”。例如,C++不需要任何Integer类,因为我们可以像下面这样使用指针和new:
int *i = new int(5);
int *j = new int(10);
*i = *j;
不像Java和C#,C++会像对待内置类型一样对待用户自定义的类:
Point2D *i = new Point2D(5, 5);
Point2D *j - new Point2D(10, 10);
*i = *j;
如果想让某个C++类具备可复制性,那么必须确保类有一个复制构造函数(copy constructor)和一个赋值运算符。当用同一种类型的对象初始化另外一个对象时,就会调用复制构造函数。对于这一操作,C++提供了两种等价的语法:
Point2D i(20, 20);
Point2D j(i); //first syntax
Point2D k=i; //second syntax
当在一个已经存在的变量上调用赋值运算符的时候,就会调用该赋值运算符:
Point2D i(5, 5);
Point2D j(10, 10);
j = i;
在定义一个类时,C++编译器会自动提供一个复制构造函数和一个赋值运算符,以用于执行成员到成员的复制。对于这个Point2D类,这样做就相当于在这个类的定义下写下了下列代码:
class Point2D
{
public:
...
Point2D(const Point2D &other)
: xVal(other.xVal),yVal(other.yVal){ }
Point &operator=(cosnt Point2D &other){
xVal = other.xVal;
yVal = other.yVal;
return *this;
}
...
private:
double xVal;
double yVal;
};
对于某些类,默认的复制构造函数和赋值运算符可能都不够用。比如当这些类使用的是动态内存时,通常就会出现这种情形。要让该类具有可复制特性,就必须自己实现它的复制构造函数和赋值运算符。
对于一些不必具有可复制特性的类,可以通过让复制构造函数和赋值运算符成为私有(private)类型而禁用它们。如果随后不小心试图去复制该类的实例,那么编译器就会报错。例如:
class BankAccount
{
public:
...
private:
BankAccount(const BankAccount &other);
BankAccount &operator = (const BankAccount &other)
};
在Qt中,许多类都被设计用在值类(value class)。它们都有一个复制构造函数和一个赋值运算符,并且通常可以在没有new的堆栈上对它们进行实例化。用于这方面的例子有QDataTime、QImage、QString类和容器类,如QList<T>、QVector<T>和QMap<K,T>。
还有其他的一些类可以归入“引用类型”的范畴中,特别是QObject以及它们的子类(如QWidget、QTimer、QTcpSocket,等等)。这些类都有虚函数、并且也都不能被复制。例如,一个QWidget表示一个具体的窗口或者屏幕上的一个控件。如果内存中有75个QWidget的实例,那么屏幕上也就有75个窗口或者控件。这些类通常都使用new操作符来实例化。