C++程序设计教程--读书笔记 第九章 对象生灭

第九章
 
一、知识点
1、对象定义和变量定义一样,当以无初始化的形式:
Date d;
定义时,若创建全局对象,则以全0位的模式表示对象。若创建局部对象,则以随机值表示对象。(P314)
 
2、构造函数的名称反映了对象创建的一般过程,它是唯一的。类的构造函数承担对象的初始化工作,它旨在使对象初值有意义。(P316)
 
3、作为对象的构造函数,其根本的使命就是创建对象实体。如果创建失败,就该让程序捕捉该异常,或者干脆终止程序的运行。因此构造函数的工作不是以对象体作为返回值,也不是以运行的失败作为继续运行的依据,构造函数的成功运行,确定了对象实体今后的操作合法性,构造函数的失败运行,预示着后继工作无法展开而必须另寻其他途径。对象定义模仿函数调用的形式:
Point x(2, 3);
决定了构造函数不应有返回值。它是类型名称引导的定义语句,不是无返回值的函数调用语句,所以,构造函数也不是无类型(void)函数。(P316)
 
4、创建对象如果不给出对象名,也就是说,直接以类名调用构造函数,则产生一个无名对象。无名对象经常在参数传递时用到。例如:
cout << Date(2003, 12, 23);
Date(2003, 12, 23)是一个对象,该对象在做了<<操作后便烟消云散了,所以这种对象一般用在创建后不需要反复使用的场合。(P318)
 
5、为了区别无初始化的对象定义和返回类对象的无参函数声明的差别,也为了保持无初始化变量定义与无初始化(无参)对象定义的一致性,规定无参对象定义语句为:(P320)
int a;        // 变量定义
int b();     // 返回整型值的无参函数声明
Data g;     // 无参对象定义
Data f();   // 返回类对象的无参函数声明
 
6、如果对构造函数进行重载,这几个重载函数都具有一个共同的初始化校验工作,也可以将该工作分离出来,单独作为一个成员函数,以避免每个重载函数都重复实现校验工作。然后在每个重载函数中调用这个函数即可。(P321)
 
7、为了方便编程,也为了与struct结构一致,系统默认,类型自定义中若无构造函数,照样可以用对象定义语句创建对象。可以这样的假想:类机制中总是为无构造函数的类默认地建立一个无参的构造函数,它除了分配对象的实体空间外,其他什么都不做。因此没有定义构造函数的类,可以看作系统给了一个默认的无参构造函数。如果用户定义的类中没有显式的定义任何构造函数,编译器就会自动为该类型生成默认构造函数,称为合成的构造函数。无参构造函数也称默认构造函数。如果手工定义了无参构造函数,或者任何其他的构造函数,则系统不再提供默认的无参构造函数。(P322)
 
8、类定义中有数据成员和成员函数,数据成员可以是内部数据类型的变量实体,也可以是对象实体。默认调用就是调用无参构造函数。(P322)
 
9、构造函数体中是不能完成对常量成员引用成员的初始化的。(P325)
 
10、在构造函数的参数列表右括号后面,花括号前面,可以用冒号引出构造函数的调用表,该调用表可以省略类型名称,但却行创建对象之职。例如:
class Student{
     string name;
     StudentID id;
public:
     Student(string n = "no name", int ssID = 0):id(ssID), name(n){
     cout << "Constructing student " << n << "\n";
}
};
构造函数的构造参数列表破例采用了对象名引导构造函数调用的形式。日期构造函数:(P325)
Date::Date(int y, int m, int d){ year = y, month = m, day = d; }
可以写成:
Date::Date(int y, int m, int d): year(y), month(m), day(d) {}
 
11、局部和静态对象是指块作用域(局部作用域)和文件作用域的对象。它们声明的顺序和它们在程序中出现的顺序是一致的。(P327)
 
12、在C中,所有的局部变量(没有对象)都是在函数开始执行时统一创建的,创建的顺序是根据变量在程序中按语句行出现的顺序。而C++ 不同,它是根据运行中定义对象的顺序来决定对象创建的顺序。而且,静态对象只创建一次。(P328)
 
13、和全局变量一样,所有全局对象在主函数启动之前,全部已被构造。因为构造过程是程序语句的执行过程,所以,可以想象在程序启动之前,已经有程序语句(构造函数)在那里被执行过了。(P328)
 
14、全局对象的创建还涉及调试问题。调试总是从main函数的位置开始的,因此在开始捕捉错误之前,错误可能已经产生了。有两种方法可以解决这个问题:一是将全局对象先作为局部对象来调试;二是在所有怀疑有错的构造函数的开头,增加输出语句,这样在获得调试控制时,就可以看到已经产生的对象构造的输出信息。不像构造局部对象那样,全局对象没有明确的控制流来表明其顺序。事实上,全局对象的创建顺序是编译器编造出来的,因而不同的编译器做法就不同。为了避免编译器实现中的不确定问题,应尽量不要设置全局对象,更不要让全局对象之间互相依赖。(P328)
 
15、成员对象以其在类中声明的顺序构造。例如,编程中想要以构造函数中构造参数表的顺序来规定对象构造的顺序,无法如愿。(P330)
 
16、全局对象、常对象、静态对象都放在全局数据区的位置。在函数中定义局部对象,则随着被调用函数的返回而析构。(P333)
 
17、应该可以以其他对象为依据来创建对象,也就是想变量在定义时复制其他变量的形式。称这种对象创建活动为拷贝构造。如果对象实体是单纯的对象本体,那么对象的拷贝构造与变量的拷贝创建并无两样,但是,对象实体若不仅仅只是对象本体时,对象的拷贝构造便有了差异。这种情况下,若进行C++的默认拷贝构造,就会只默认拷贝构造了对象本体,出现异常。为了达到对象实体也就是对象整体复制目的,就需要另外定义一个拷贝构造函数,以覆盖默认的拷贝构造函数。(P336)
 
18、自定义拷贝构造函数名也是类名,它是构造函数的重载,一旦自定义了拷贝构造函数,默认的拷贝构造函数就不再起作用了。拷贝构造函数的参数必须是类对象的常量引用:
Person (const Person & s);
因为对象复制的语义本身尚处在当前定义当中,参数传递若为传值形式,则对象复制操作调用的拷贝构造函数在哪里?所以只能是引用或者指针。(P336)
 
19、编程经验:自动以的对象作为参数传递,能用引用就尽量使用引用,能用常量引用的尽量使用常量引用。(P336)
 
20、自定义拷贝构造函数在对象本体与对象实体不一致时,便是需要的,否则无此必要。(P336)
 
21、当对象本体与对象实体一致时,其拷贝称为浅拷贝;当对象本体与对象实体不一致时,其拷贝称为深拷贝。深拷贝需要做动态内存分配的工作。因此,当对象的生命期终止时,也就是对象本体消失时,需要做动态内存的释放工作。(P337)
 
22、人为的动态内存释放工作由析构函数来完成,它的意义是做关于对象本体失效之前瞬间的善后工作。一般来说,需要定义拷贝构造函数的类也需要定义析构函数,不需要拷贝构造函数的类,也无须定义析构函数。所以析构函数与拷贝构造函数是成对出现。(P337)
 
23、析构函数没有返回类型,没有参数。它只是将成员中的一些数据指针所指向的动态内存空间释放而已。析构函数的函数形式是唯一的,没有重载的析构函数。它在对象的生命期行将结束的瞬间,由系统自动调用。对象构造与析构的关系式栈数据结构中的入栈与出栈的关系,所以对象析构的顺序与对象的创建的顺序正好相反。(P337)
 
24、5.0/8在计算时,编译器会将8自动转换成double。对于类类型,其自动转换的功能必须编程实现。那就是定义含一个参数的构造函数。对象拷贝就是对象赋值。对象赋值操作的返回必须是引用返回,这是为了与赋值操作符的语义一致。(P342)
 
25、在对象捆绑成员函数的处理过程中,我们始终默认对象中的成员,所操作的数据成员都是针对当前对象的。但如果要将当前对象赋给其他对象,或者以当前对象作为结果返回,或者要判断当前对象是否就是赋值操作的源对象,即赋值对象要与被赋对象比较存放的位置,那该怎么办呢,C++为此规定了一个关键字this,专门用于表示当前对象的地址。在赋值操作符定义中,我们便用它方便地表示返回对象。(P342)
posted on 2012-05-01 09:58  谷堆旁边  阅读(534)  评论(0编辑  收藏  举报