nullC++程序设计教程(第二版)C++程序设计教程(第二版)第九章 对象生灭
Chapter 9 Object Birth & Death 清华大学出版社 钱 能第九章
内容
财务内部控制制度的内容财务内部控制制度的内容人员招聘与配置的内容项目成本控制的内容消防安全演练内容
第九章内容构造函数设计 ( Constructor Design )
构造函数重载 ( Constructor Overload )
类成员初始化 ( Class Member Initializations )
构造顺序 ( Constructing Order )
拷贝构造函数 ( Copy Constructors )
析构函数 ( Destructors )
转型与赋值 ( Conversion & Assignment ) 1. 构造函数设计 ( Constructor Design ) 1. 构造函数设计 ( Constructor Design ) 初始化要求:对象与变量的不同在于对象对应于事物,要求从诞生之时起便有明确的意义.
封装性要求:初始化不是简单的
参数
转速和进给参数表a氧化沟运行参数高温蒸汽处理医疗废物pid参数自整定算法口腔医院集中消毒供应
与成员对应,而是联系参数到成员的过程.
构造函数名:该过程产生对象,而不是捆绑对象的成员函数调用,因而它是特殊的成员函数
形式:与变量的定义形式保持一致.构造函数原则上不能失败,也没有返回形式
例外:一次性对象构造,没有对象名,与强制转换的形式一致,因而它是一个特定类型的对象.2. 构造函数重载 ( Constructor Overload ) 2. 构造函数重载 ( Constructor Overload ) 构造函数可以重载,也可以参数默认:
class Date{
public:
Date(const string& s);
Date(int y=2003, int m=12, int d=1);
// ...
};
int main(){
Date d(“2006-12-26”);
Date e(2000, 12, 25);
Date f(2001, 10);
Date g(2002);
Date h();
// ...
}null若类中没有定义构造函数,则系统会默认定义一个无参空函数:
class Date{
public:
// 相当于定义了Date(){}
};
int main(){
Date d; // ok
// ...
}null任何其他的构造函数定义,都将阻止默认无参空函数的产生:
class Date{
public:
Date(int y, int m, int d){}
// ...
};
int main(){
Date d; // error
// ...
}3. 类成员初始化 ( Class Member Initializations ) 3. 类成员初始化 ( Class Member Initializations ) class StudentID{
int a;
public:
StudentID(){
a = 1;
cout<<“StudentId: "<
Date e
对象创建的运行顺序为:
Date enull同一工程不同代码文件全局对象的创建没有明确顺序规定.
对策:不要让不同文件的全局对象互为依赖.因为依赖具有先后性,而其全局对象的创建不能保证该依赖性发挥作用.
全局对象在main函数启动之前生成,而调试则在main函数启动之后.
对策:调试时,应先将全局对象作为局部对象来运行观察.或者,在构造函数中添加输出语句来观察运行过程.null成员对象的构造顺序按类定义的出现顺序,最后执行自身构造函数:
class A{
B b;
C c;
D d;
public:
A(){}
// ...
};
int main(){
A a;
}
则构造顺序为b>c>d,然后执行A的构造函数的花括号体{}null全局数据区:
全局对象,静态全局对象,静态局部对象,常对象
类的静态数据成员也存放在该数据区
栈区:
局部对象
(根据不同编译器的实现方法,临时对象可能在栈区,也可能在动态存储区,也可能一部分在栈区,一部分在动态存储区)
动态存储区(也称堆区):
用new申请的对象
除此之外,还可以指定特殊地址空间,存放对象构造位置5. 拷贝构造函数 ( Copy Constructors ) 5. 拷贝构造函数 ( Copy Constructors ) 对象本体与对象实体:
对象本体也是对象主体,对象实体则还包括属于对象的衍生物,如,某个人体是人类对象的主体,然而某人还拥有父母,房产等属于某人的世系或资产,描述人的属性不仅仅只是人体数据.
从形式上看,对象除了包括数据成员,还包括指向数据的指针.null拷贝构造函数:
以本类对象为常量引用参数的构造函数:
class Date{
public:
Date();
Date(const Date& d);
// . . .
};
Date x; //调用无参构造函数
Date y(x); //调用拷贝构造函数null默认拷贝构造函数:
若类中没有定义拷贝构造函数,则系统会悄悄定义一个默认空拷贝构造函数:
Date(const Date& d){}
默认拷贝构造函数体一定是空的.
空拷贝构造函数负责将传递的对象到新创的对象做对象本体的位对位拷贝.(甚至连指针值都相等,即与参数对象拥有共同的资源)
拷贝构造函数体的工作不负责位对位对象复制,一般来说,它负责资源分配和由此而来的指针修改.null拷贝构造函数体的工作不负责位对位对象复制,一般来说,它负责资源分配和由此而来的指针修改.
class Person{
char* pName;
public:
Person(char* pN="noName"){
pName = new char[strlen(pN)+1];
if(pName) strcpy(pName,pN);
}
Person(const Person& s){
pName = new char[strlen(s.pName)+1];
if(pName) strcpy(pName, s.pName);
}
~Person(){
delete[] pName;
}
};6. 析构函数 ( Destructors )6. 析构函数 ( Destructors )对象结束其生命时,会被系统悄悄地销毁(析构).即对象本体空间与名字脱离关系.
对象结束生命时,若对象本体与对象实体不同,则需要人为地进行资源释放,以保证对象本体失效之前,资源被收回.null定义析构函数的目的:
由于对象本体与实体不同,所以要进行对象占有资源的释放工作.
一般来说,一个类,若有人为定义的拷贝构造函数,则也应该定义析构函数.因为对象创建中有资源要获得分配,则对象失效前必应先释放资源.7. 转型与赋值 ( Conversion & Assignment ) 7. 转型与赋值 ( Conversion & Assignment ) 对象转型
一个构造函数,含有一个其他数据类型的参数,显然其意义为,用该参数类型的值可以创建本对象.从另一方面看,参数类型的值可以转换为本对象.
class Student{
public:
Student(const string& n);
// ...
};
void fn(Student& s);
int main(){
string t=“jenny”;
fn(t); // 参数为string,却能匹配Student类型
}null对象转型的规则:
只会尝试含有一个参数的构造函数
如果有二义性,则会放弃尝试
推导是一次性的,不允许多步推导
fn(“Jenny”)不能匹配
void fn(const Student& s);
因为:”Jenny”
-> string
-> Student
经历了两步.null对象赋值即对象拷贝:两个已经存在的对象之间的复制
Person d, g;
d = g; // 对象赋值
对象赋值便是使用类中的赋值操作符.
如果类中没有定义赋值操作符,则系统悄悄地定义一个默认的赋值操作符:
Person& operator=(const Person& p){
memcpy(*this, *p, sizeof(p));
}null当对象本体与对象实体不同时,则对象赋值操作符与拷贝构造函数一样,必须自定义:
class Person{
char* pName;
public:
Person(char* pN="noName");
Person(const Person& s);
Person& operator=(const Person& s){
if(this==&s) return s;
delete[] pName;
pName = new char[strlen(s.pName)+1];
if(pName) strcpy(pName,s.pName);
return *this;
}
~Person(){
delete[] pName;
}
};定义赋值操作符:
1排除客体对象与本对象同一的情况
2释放本对象的资源
3申请客体对象相同大小的资源空间
4拷贝客体对象的资源到本对象