3 类和对象进阶
3.1 复制构造函数(拷贝构造函数)
1). 基本概念
X::XX(X&) 或 X::X(cons X&)
用户未定义构造函数时类有2个构造函数,分别为:缺省的无参数的构造函数,缺省的复制构造函数(参数为该类的对象);
用户定义了自己的复制构造函数时,将不存在默认的复制构造函数。zpcj?这是会存在缺省的无参数的构造函数吗?
不允许有形如X::X(X)的构造函数。
复制构造函数起作用的三种情况:
a. 用一个对象去初始化同类的另一个对象时:
Complex c1;
Complex c2(c1);
Complex c3 = c1; // 初始化,不是赋值
b. 函数(可以不是类成员函数)的一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数被调用。如:
void Func(A a1) {}
int main{
A a2;
Func(a2); // 此时a1是否与a2相等要分情况:当用户未定义A的赋值构造函数时,相等(以a2为复制构造函数的参数,调用A的复制构造函数,此时缺省的复制构造函数会用a2来初始化a1);当用户定义了A的复制构造函数时,不一定(需要看用户定义的复制构造函数内部是否进行了数据成员的拷贝)。
return 0;
}
c. 当函数的返回值是类A的对象时,会调用A的复制构造函数。如:
class A{
public:
int v;
A(int n) {v = n;}
A(const A& a){
v = a.v;
cout << "Copy Constructor called" << endl;
}
};
A Func(){
A b(4);
return b; // 以b为复制构造函数的参数,调用A的复制构造函数
}
int main{
cout << Func().v << endl;
return 0;
}
3.2 类型转换构造函数
目的:实现类型的自动转换。
特点:只有一个参数;不是复制构造函数。
如:
class Comples{
public:
double real, imag;
Complex(int i){ // 类型转换构造函数
cout << "intConstructor called" << endl;
real = i; imag = 0;
}
Complex(double r, double i){
real = r; imag = i;}
};
int main(){
Complex c1(7, 8);
Complex c2 = 12; // 是类对象的初始化,不是赋值;以12为参数,调用类型转换构造函数
c1 = 9; // 赋值语句;先将9自动转换成一个临时的Complex对象,然后...
cout << c1.real << "," << c1.imag << endl;
return 0;
3.3 析构函数(一个类最多只有一个析构函数)
编译器生成的缺省析构函数不会进行内存释放登操作。
关于作用域、构造函数与析构函数调用时机的例子,请分析下面代码:
class Demo{
int id;
public:
Demo(int i){
id = i;
cout << "id=" << id << "Constructed" << endl;
}
~Demo(){
cout << "id=" << id << "Destructed" << endl;
}
};
Demo d1(1);
void Func(){
static Demo d2(2);
Demo d3(3);
cout << "Func" << endl;
}
int main(){
Demo d4(4);
d4 = 6;
cout << "main" << endl;
{ Demo d5(5); }
Func();
cout << "main ends" << endl;
return 0;
}
上述代码的输出为:
id=1 Constructed
id=4 Constructed
id=6 Constructed // d4=6; 会以6为参数调用Demo的转换构造函数,同时生成临时对象
id=6 Destructed // 赋值完成后释放该临时构造函数
main
id=5 Constructed
id=5 Destructed // 离对象最近的一对花括号是该对象的作用域,标识了该对象的生命周期
id=2 Constructed
id=3 Constructed
Func
id=3 Destructed
main ends
id=5 Destructed
id=2 Destructed
id=1 Destructed
3.4 静态成员变量和静态成员函数
sizeof不会计算静态成员变量。
可以通过类名::成员名,对象名.成员名,指针->成员名,引用.成员名的方式访问静态成员。
静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。
设计静态成员这种机制的目的是将与类紧密相关的全局变量、全局函数放到类内部,便于理解和维护。
非静态成员函数可以访问静态成员函数,静态成员函数中不可访问非静态成员变量,也不能调用非静态成员函数。
必须在定义类的文件中对静态成员变量进行一次说明或初始化。
3.5 成员对象和封闭类
成员对象:一个类的成员变量是另一个类的对象。
封闭类:包含成员对象的类。
举例:
class CTyre{
private:
int radius;
int width;
public:
CTyre(int r, int w) : radius(r), width(w) {}
};
class CEngine{
};
class CCar{
private;
int price;
CTyre tyre;
CEngine engine;
public:
CCar(int p, int tr, int tw);
};
CCar::CCar(int p, int tr, int w) : price(p), tyre(tr, w) {}
int main()
{
CCar car(20000, 17, 225);
return 0;
}
如果不定义CCar的构造函数,则CCar car; 编译出错,编译器不知道car.tyre如何初始化。
调用顺序:
当封闭类对象生成时:先执行所有成员对象的构造函数;再执行封闭类的构造函数。
成员对象的构造函数调用顺序:和成员对象在类中的说明顺序一致,与在成员初始化列表中出现的顺序无关。
当封闭类的对象消亡时:先执行封闭类的析构函数;再执行成员对象的析构函数。
3.6 友元
友元机制成为必须的情况:
当该操作的左操作数为与所创建的类不同的类型且该操作必须直接访问该类的实体变量时。
举例:
class CCar;
class CDriver{
public:
void ModifyCar(CCar *pCar);
};
class CCar{
private:
int price;
friend int MostExpensiveCar(CCar cars[], int total); // 外部函数MostExpensiveCar可以访问CCar的私有成员
friend void CDriver::ModifyCar(CCar *pCar); // CDriver的ModifyCar函数时CCar的友元。这时,CDriver的ModifyCar成员函数可以访问CCar的私有成员price
};
友元类:
class CCar{
private:
int price;
friend class CDriver; // CDriver是CCar的友元类。此时,CDirver中的任何成员函数都可以访问CCar的私有成员
};
class CDriver{
public:
CCar myCar;
void ModifyCar() {
myCar.price += 1000;
}
};
note: 友元类的关系不能传递,也不能继承。
3.7 this指针
this指针的作用:指向成员函数所作用的对象。
举例:
class A{
int i;
public:
void Hello() { cout << "hello" << endl; }
};
int mian(){
A* p = NULL;
p->Hello(); // 没问题。
}
按照this指针的作用,指向成员函数所作用的对象,因此翻译成C程序变为:
struct A{
int i;
};
void Hello(A* this) { cout << "hello" << endl; }
int main(){
A* p = NULL;
Hello(p);
}
note:
静态成员函数不能使用this指针,因为静态成员函数并不具体作用于某个对象。
3.8 常量对象、常量成员函数和常引用
常量成员函数不能修改非静态成员变量,也不能调用同类的非常量成员函数(这个非常量成员函数可能修改成员变量),静态成员函数除外(静态成员函数不会访问非静态成员变量)。
常量对象上不能执行非常量长远函数。
两个成员函数,返回值、名字和参数表都一样,但是一个是const,一个不是,算是重载。
3.9 others
虚函数与纯虚函数
纯虚函数:virtual double wavelength() const =0;
定义纯虚函数的两个结果:
包含了纯虚函数的类成为一个抽象类,意味着不允许创建该类的对象实例;
为了编译不出错,任何抽象类的派生类都必须实现纯虚函数,否则该派生类也将被视为纯虚函数。
使用vector定义二维数组:
vector<vector<double> > table(row, vector<double>(column,0.0));
carnegie-mellon大学的cmu图形库
岩石的分类:
火成岩,玄武岩,花岗岩,黑曜石
变形岩,高温高压下行形成,大理岩,石英岩,板岩
沉积岩,白云石,石灰石,砂岩,页岩
3.1 复制构造函数(拷贝构造函数)
1). 基本概念
X::XX(X&) 或 X::X(cons X&)
用户未定义构造函数时类有2个构造函数,分别为:缺省的无参数的构造函数,缺省的复制构造函数(参数为该类的对象);
用户定义了自己的复制构造函数时,将不存在默认的复制构造函数。zpcj?这是会存在缺省的无参数的构造函数吗?
不允许有形如X::X(X)的构造函数。
复制构造函数起作用的三种情况:
a. 用一个对象去初始化同类的另一个对象时:
Complex c1;
Complex c2(c1);
Complex c3 = c1; // 初始化,不是赋值
b. 函数(可以不是类成员函数)的一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数被调用。如:
void Func(A a1) {}
int main{
A a2;
Func(a2); // 此时a1是否与a2相等要分情况:当用户未定义A的赋值构造函数时,相等(以a2为复制构造函数的参数,调用A的复制构造函数,此时缺省的复制构造函数会用a2来初始化a1);当用户定义了A的复制构造函数时,不一定(需要看用户定义的复制构造函数内部是否进行了数据成员的拷贝)。
return 0;
}
c. 当函数的返回值是类A的对象时,会调用A的复制构造函数。如:
class A{
public:
int v;
A(int n) {v = n;}
A(const A& a){
v = a.v;
cout << "Copy Constructor called" << endl;
}
};
A Func(){
A b(4);
return b; // 以b为复制构造函数的参数,调用A的复制构造函数
}
int main{
cout << Func().v << endl;
return 0;
}
3.2 类型转换构造函数
目的:实现类型的自动转换。
特点:只有一个参数;不是复制构造函数。
如:
class Comples{
public:
double real, imag;
Complex(int i){ // 类型转换构造函数
cout << "intConstructor called" << endl;
real = i; imag = 0;
}
Complex(double r, double i){
real = r; imag = i;}
};
int main(){
Complex c1(7, 8);
Complex c2 = 12; // 是类对象的初始化,不是赋值;以12为参数,调用类型转换构造函数
c1 = 9; // 赋值语句;先将9自动转换成一个临时的Complex对象,然后...
cout << c1.real << "," << c1.imag << endl;
return 0;
3.3 析构函数(一个类最多只有一个析构函数)
编译器生成的缺省析构函数不会进行内存释放登操作。
关于作用域、构造函数与析构函数调用时机的例子,请分析下面代码:
class Demo{
int id;
public:
Demo(int i){
id = i;
cout << "id=" << id << "Constructed" << endl;
}
~Demo(){
cout << "id=" << id << "Destructed" << endl;
}
};
Demo d1(1);
void Func(){
static Demo d2(2);
Demo d3(3);
cout << "Func" << endl;
}
int main(){
Demo d4(4);
d4 = 6;
cout << "main" << endl;
{ Demo d5(5); }
Func();
cout << "main ends" << endl;
return 0;
}
上述代码的输出为:
id=1 Constructed
id=4 Constructed
id=6 Constructed // d4=6; 会以6为参数调用Demo的转换构造函数,同时生成临时对象
id=6 Destructed // 赋值完成后释放该临时构造函数
main
id=5 Constructed
id=5 Destructed // 离对象最近的一对花括号是该对象的作用域,标识了该对象的生命周期
id=2 Constructed
id=3 Constructed
Func
id=3 Destructed
main ends
id=5 Destructed
id=2 Destructed
id=1 Destructed
3.4 静态成员变量和静态成员函数
sizeof不会计算静态成员变量。
可以通过类名::成员名,对象名.成员名,指针->成员名,引用.成员名的方式访问静态成员。
静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。
设计静态成员这种机制的目的是将与类紧密相关的全局变量、全局函数放到类内部,便于理解和维护。
非静态成员函数可以访问静态成员函数,静态成员函数中不可访问非静态成员变量,也不能调用非静态成员函数。
必须在定义类的文件中对静态成员变量进行一次说明或初始化。
3.5 成员对象和封闭类
成员对象:一个类的成员变量是另一个类的对象。
封闭类:包含成员对象的类。
举例:
class CTyre{
private:
int radius;
int width;
public:
CTyre(int r, int w) : radius(r), width(w) {}
};
class CEngine{
};
class CCar{
private;
int price;
CTyre tyre;
CEngine engine;
public:
CCar(int p, int tr, int tw);
};
CCar::CCar(int p, int tr, int w) : price(p), tyre(tr, w) {}
int main()
{
CCar car(20000, 17, 225);
return 0;
}
如果不定义CCar的构造函数,则CCar car; 编译出错,编译器不知道car.tyre如何初始化。
调用顺序:
当封闭类对象生成时:先执行所有成员对象的构造函数;再执行封闭类的构造函数。
成员对象的构造函数调用顺序:和成员对象在类中的说明顺序一致,与在成员初始化列表中出现的顺序无关。
当封闭类的对象消亡时:先执行封闭类的析构函数;再执行成员对象的析构函数。
3.6 友元
友元机制成为必须的情况:
当该操作的左操作数为与所创建的类不同的类型且该操作必须直接访问该类的实体变量时。
举例:
class CCar;
class CDriver{
public:
void ModifyCar(CCar *pCar);
};
class CCar{
private:
int price;
friend int MostExpensiveCar(CCar cars[], int total); // 外部函数MostExpensiveCar可以访问CCar的私有成员
friend void CDriver::ModifyCar(CCar *pCar); // CDriver的ModifyCar函数时CCar的友元。这时,CDriver的ModifyCar成员函数可以访问CCar的私有成员price
};
友元类:
class CCar{
private:
int price;
friend class CDriver; // CDriver是CCar的友元类。此时,CDirver中的任何成员函数都可以访问CCar的私有成员
};
class CDriver{
public:
CCar myCar;
void ModifyCar() {
myCar.price += 1000;
}
};
note: 友元类的关系不能传递,也不能继承。
3.7 this指针
this指针的作用:指向成员函数所作用的对象。
举例:
class A{
int i;
public:
void Hello() { cout << "hello" << endl; }
};
int mian(){
A* p = NULL;
p->Hello(); // 没问题。
}
按照this指针的作用,指向成员函数所作用的对象,因此翻译成C程序变为:
struct A{
int i;
};
void Hello(A* this) { cout << "hello" << endl; }
int main(){
A* p = NULL;
Hello(p);
}
note:
静态成员函数不能使用this指针,因为静态成员函数并不具体作用于某个对象。
3.8 常量对象、常量成员函数和常引用
常量成员函数不能修改非静态成员变量,也不能调用同类的非常量成员函数(这个非常量成员函数可能修改成员变量),静态成员函数除外(静态成员函数不会访问非静态成员变量)。
常量对象上不能执行非常量长远函数。
两个成员函数,返回值、名字和参数表都一样,但是一个是const,一个不是,算是重载。
3.9 others
虚函数与纯虚函数
纯虚函数:virtual double wavelength() const =0;
定义纯虚函数的两个结果:
包含了纯虚函数的类成为一个抽象类,意味着不允许创建该类的对象实例;
为了编译不出错,任何抽象类的派生类都必须实现纯虚函数,否则该派生类也将被视为纯虚函数。
使用vector定义二维数组:
vector<vector<double> > table(row, vector<double>(column,0.0));
carnegie-mellon大学的cmu图形库
岩石的分类:
火成岩,玄武岩,花岗岩,黑曜石
变形岩,高温高压下行形成,大理岩,石英岩,板岩
沉积岩,白云石,石灰石,砂岩,页岩