cout<<"global datum address:"<<&data< cout<<"local datum address:"<<&i< cout<<"pointer address:"<<&ptr< cout<<"heap address:"< cout<<"datum in heap:"<<*ptr< delete ptr; } /* Results: code address:0x00401014 global datum address:0x00428D64 local datum address:0x0012FF7C pointer address:0x0012FF78 heap address:0x00431AE0 datum in heap:8 */ data位于数据区、i位于栈区、ptr位于栈区、*ptr位于堆区、main( )位于代码区。 第三章思考题题解 (一)选择填空: 答案:(1) C (2) B (3) D (4) A (5) C (6) A (7) D (8) B (9) D。 (1) 关于类定义格式的描述中,( C )是错的。 ( C )是错的。 A. 一般类的定义格式可分为类的声明(也称类的接口)部分和成员函数定义 (也称类的实现)部分共两部分,有时这两部分也可合并为一个统一的类的定义。 格式可分为类的声明(也称类的接口)部分和成员函数定义(也称类的实现)部分共两部分,有时这两部分也可合并为一个统一的类的定义。 举例如下: (1)统一的类的定义: class integ { int j; //私有属性数据 public: integ( ) { j = 6; } //构造函数定义 int sq() { return j*j; } //成员函数定义 }; (2)(A)类的声明(也称类的接口)部分: class integ { int j; //私有属性数据 public: integ( ); //构造函数原型 int sq( ); //成员函数原型 }; 只包括成员函数的函数原型,也可能包括构造函数和析构函数的函数定义。 但不包括成员函数的定义部分(即函数体)。 (B)函数定义部分(很可能在另一个文件内) integ::integ ( ) { j = 6; } //构造函数体 integ::int sq( ) { return j*j; } //成员函数体 B. 类中一般包含有数据成员和成员函数。 . 类中一般包含有数据成员和成员函数。 C. 类对象的内存存储内容中,所有数据(包括静态数据成员和非静态数据成 员)都存于内存数据区内。不对!只有静态数据成员存于数据区内,而 非静态数据成员则存于栈区内。 D. 类对象的成员函数的代码部分都存于内存代码区内。 (2) 关于类成员的描述中,( B)是错的。 的描述中,( B)是错的。 A.类中可以说明或定义一个或多个成员函数。 B.类中的成员函数的定义只能放在类体外。 错!成员函数的定义一般放在类体外,但也可放在类体内。 C. 在类体外使用数据成员时,须使用类名及作用域运算符(::)来指明 (限定)该数据成员(例如base::a)。 (3) 在类的静态成员的描述中,(D )是错的。 在类的静态成员的描述中,(D )是错的。 A.静态成员分为静态数据成员和静态成员函数。 B. 静态数据成员声明后必须在类体外进行说明或定义,以便初始化。例如: class counter { static int count; //用于表示已建立的对象数 ………… public: counter ( ) {……}//不负责静态数据的初始化 ………… }; int counter::count; // this should not be omitted C. 静态数据成员初始化不使用其构造函数。(因为必须在类体外初始化) D. 静态成员函数可以不使用类名和作用域运算符而直接访问该类的某个具 体对象的非静态成员。 (错!因静态成员函数没有this指针)。 由于静态成员函数中没有this指针,无法判断当前是哪一个对象。也就无法找到某一对象的数据成员。因此,静态成员函数要识别对象,只能依靠参数传递对象名来识别对象。 例如第三章§3.6.2.2[例3],以及[例4]的static int set_total_length (string obj) {……} (4) 在友元的描述中,( A)是错的: A. 一个类的友元函数是该类的成员函数。(错!它不是成员函数, 而是独立于类以外的函数,但被授权能直接访问类中的私有成员,所以在类体外调用友元函数时,不必也不能使用类名及作用域运算符(::)来对它加以指明或限定)。 B. 友元函数可直接访问类中的私有成员。 C.友元函数损害封装性,应限用。 D.友元类中的所有成员函数都是友元函数。 (5) 在有关类和对象的描述中,( D )是错的: A. 对象是类的一个实例。 B. 任何一个对象只能属于一个类。 C. 类和对象的关系与数据类型和变量的关系相似。(例如:int j; 与base obj;) D. 一个类只能有一个对象。(错!对象数量并无限制。正如整型 变量的数量毫无限制一样。)。 (6) 下列关于对象数组的描述中,(A)是错误的。 A. 对象数组只能赋初值而不能再赋值。(错!可以无数次赋值,和普通数组相同。事实上,有些情况下,建立数组时无法初始化,必须再次赋值。) B. 对象数组的下标(序号)从0开始。 C. 对象数组的每个元素都是相同类的对象。 D. 对象数组的数组名是一个常量指针。 (7) 下列关于子对象的描述中,( D )是错误的。 A. 子对象是另一个类(复合类)的一个数据成员。 B. 子对象不可以是本复合类的对象而应是其它类的对象。 C. 对子对象的初始化必须通过构造函数中的初始化列表。 D. 一个类中只能含有一个子对象作其成员。(错!数量不限)。 (8) 在new运算符的下列描述中,(B )是错误的。 A. 它可以为创建对象或对象数组而动态地在堆区内分配空间。 B. 用它创建对象数组时必须指定初始值。(错!事实上无法指定,最多只能使用 缺省值)。如果必须在创建对象数组时指定初始值,则可不使用new在堆区内分配空间,而使用普通方式,在栈区内创建对象数组。如下: class base {…… public: base(int k, int j) {……} }; void main( ) { base arr[3]={ base(5,6), base(7,8), base(9,10) };这类似于int arr[3] = { 7, 8, 9 }; ) C. 当构造函数使用它在堆区内创建对象或对象数组时,在程序结束前应使用运算符delete来释放所分配的堆区空间。 (9) 在delete运算符的下列描述中,( D )是错误的。 A. 用它可以释放先前用new运算符创建的为对象或对象数组分配的堆区空间。 B. 用它释放一个对象时,它作用于一个new所返回的指针。 C. 用它释放一个对象数组时,它作用的指针名前须加下标运算符[ ]。 D. 用它释放一个对象的堆区空间时,它同时删除new运算符所使用的指针。(错!事实上指针没有删除,仍然保留,可再次使用)。 (二)判断下列各种描述是否正确,对者划√,错者划 述是否正确,对者划√,错者划 否正确,对者划√,错者划 ,错者划 者划 答案:(1)√(2)√(3) (1) 建立一个类的对象时,系统将其非静态数据成员存储于该对象的栈区内。而该类的静态数据成员和成员函数代码则分别存储于数据区和代码区内。√ (2) 如果一个成员函数只访问一个类的静态数据成员,则将该成员函数定义为静态成员函数将更方便。√(此做法的优点是可在建立对象之前就调用静态函数来访问静态数据,参阅第三章§3.6.2.2[例1]程序static_fun_3.cpp。其中主函数为:void main() { counter::show( ); //如不将函数counter::show( )定义为静态函数,则将如下出错:// error C2352: 'counter::show' : // illegal call of non-static member function counter *ptr1 = new counter; …… } ) (3) 可在类的构造函数的初始化列表中对该类的静态数据成员进行初始化。 (静态数据成员只能在类体之内声明,并在类体之外初始化。例如int [static] s;自动初始化为零, 或int [static] s(3); 初始化为一定值) (三)给出以下程序的运行结果: // exer_3_3.cpp #include class C { static int c; public: C( ) { cout<<+ +c< static int Getc( ) { return c; } ~C( ) { cout< }; int C::c; //自动初始化为零 void main( ) { C c1, c2, c3; cout< } /* Results: 1 2 3 3 // C::Getc( )所显示内容 3 2 1 */ (四)给出以下程序的运行结果: // exer_ch3_4.cpp #include class M { int m1, m2; public: M(int i, int j) { m1=i; m2=j; } void Sum(M a, M b) { m1=a.m1+b.m1; m2=a.m2+b.m2; } void print( ) { cout<<"m1="< }; void main() { M a(30, 70); M b(a); // 或M b = a; b.Sum(a, b);// 类似于b=a+b b.print( ); } /* Results: m1=60,m2=140 */ (五)给出以下程序的运行结果: // exer_ch3_5.cpp #include class AA { public: int a; AA (int x) { a = x; cout<<"Cons-"< ~AA ( ) { cout<<"Des-"< }; void inc_by_value(AA obj) //形参是class AA的对象obj { obj.a++; } void inc_by_refer(AA & obj) //形参是class AA的对象obj的引用,实即指针 { obj.a++; } void main() { AA obj(50); inc_by_value(obj); cout<<"inc_by_value:a="< inc_by_refer(obj); cout<<"inc_by_reference:a="< } /* Results: Cons-50(建立obj时) Des-51(退出函数inc_by_value时删除临时对象) inc_by_value:a=50 inc_by_reference:a=51 Des-51(退出主函数时) */ (六)给出以下程序的运行结果: // exer_ch3_6.cpp #include class figure { double d; public: figure ( double m ) { d = m; cout<<"CONS-sub:"< }; class point { int x; figure sub_obj; // sub_obj为子对象 public: point (int a, double d) : x(a), sub_obj(d) //将子对象初始化 { cout<<"CONS:"< ~point( ) { cout<<"Des:"< }; void main() { point objp1(123, 98.76); } /* Results: CONS-sub:98.76(建立objp1时) CONS:123 Des:123(退出主函数时删除objp1) Des-sub:98.76 */ (七)判断下列描述是否正确,对者划√,错者划 。 答案:(1)√ (2)√(3)√ (4)√(5) (1) 对象和指向对象的指针都可以用作函数参数。√ (2) 对象数组的元素必须是同一个类的对象。√ (3) 一维对象指针数组的每个元素应该是同一个类的不同对象的地 址值。√ (4) 运算符new可以在堆区内创建变量或对象,也可以创建变量数组 或对象数组。√ (5) 子对象的初始化可在复合类的构造函数的函数体内进行。 (应在构造函数的初始化列表中而不是在函数体内进行,见第六题)(八)给出以下程序的运行结果,并画出对象obj的栈区存储内容。#include class B { int b1, b2; public: B(int i, int j); void printB( ); }; //这是典型的类的接口(声明) B::B(int i, int j) { b1 = i; b2 = j; } void B::printB( ) {