Classes: A Deeper Look, Part 2 Chapter 10 Classes: A Deeper Look, Part 2 东方 fdong@seu.edu.cn
OBJECTIVES To specify const (constant) objects and const member functions. To create objects composed of other objects. To use friend functions and friend classes. To use the this pointer. To create and destroy objects dynamically with operators new and delete, respectively. To use static data members and member functions.
Topics 10.1 Introduction 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 10.8 Data Abstraction and Information Hiding
10.1 Introduction const对象 类的组合(composition): 一个类以其他类的对象作为成员 友元(friend)函数: 非成员函数如何访问类的私有成员? this指针: 所有非static函数的隐含实参 动态内存管理: new和delete运算符 静态static类成员
Topics 10.1 Introduction 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 10.8 Data Abstraction and Information Hiding
10.2 const (Constant) Objects and const Member Functions—常量对象 2018年11月29日9时8分 10.2 const (Constant) Objects and const Member Functions—常量对象 目的:一些对象要求是可修改的,另一些则不希望被修改 const Time noon( 12, 0, 0 ); 将变量和对象声明为const可提高性能 要求:不允许调用普通成员函数,仅能调用noon对象的const member function (常成员函数)
10.2 const (Constant) Objects and const Member Functions—常成员函数 常成员函数(两处都要声明) prototype: void printUniversal() const; definition: void Time::printUniversal() const { … } 要求: 不能修改本对象的数据成员 不能调用本对象其它non-const成员函数
10.2 const (Constant) Objects and const Member Functions—访问权限 Access const √ non-const X
10.2 const (Constant) Objects and const Member Functions class Time { public: Time( int = 0, int = 0, int = 0 ); void setTime( int, int, int ); void setHour( int ); void setMinute( int ); void setSecond( int ); int getHour() const; int getMinute() const; int getSecond() const; void printUniversal() const; void printStandard(); private: int hour; int minute; int second; }; P401 10.1-3
10.2 const (Constant) Objects and const Member Functions void Time::test(Time &another) const { minute = 20; // 不能修改object printStandard(); // 不能调用non-const成员函数 another.minute = 20; // OK,非同一object another.setHour(6); // OK }
10.2 const (Constant) Objects and const Member Functions 建议: 所有不更改object成员的函数均声明为const成员函数 注意: (第20行)成员函数是否为常成员函数,不仅取决于它不修改对象、不调用non-const成员函数,而且必须显式地声明为const! 构造函数、析构函数不能声明为const 可以调用非const成员函数初始化const对象(为什么?) 对象的常量特性体现在初始化(构造)后、析构之前。
10.2 const (Constant) Objects and const Member Functions 2018年11月29日9时8分 10.2 const (Constant) Objects and const Member Functions 复习const变量的操作 定义const型常量 const int a = 100; 注意:一定要在定义时对其初始化,不能赋值 同define的区别(从编译器角度&从存储方式角度) const型指针 const int *Point; int * const Point = &x; const int *const Point = &y; 思考:对于const数据成员的初始化 同define的区别(从编译器角度&从作用域角度) const定义的只读变量在程序运行过程中只有一份拷贝(因为它是全局的只读变量,存放在静态区),而#define定义的宏常量在内存中有若干个拷贝,不会分配内存。 #define宏是在预编译阶段进行替换,而const修饰的只读变量是在编译的时候确定其值。 //指向数据是常量 //指针本身是常量 //两者兼备
总结:const作用 可以定义const常量,具有不可变性; 便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患; 可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改; 可以保护被修饰的东西,防止意外的修改,增强程序的健壮性; 提高了效率(编译器对常量进行优化)
Const数据成员初始化的问题 数据成员在类定义体内只能定义不能初始化,初始化过程需要在构造函数中进行 Const变量需要在定义时初始化
10.2 const (Constant) Objects and const Member Functions—初始化列表 Member Initializer – constructor initializer list(构造函数初始化列表) 所有的类成员都可以用构造函数初始化列表进行初始化,而以下情况只能如此: const data member (例外?const static integer) reference data member 引用类型的数据成员 member objects, 数据成员是其他类(未提供缺省构造函数)的对象* 继承类的基类base class*
class Increment { public: Increment( int c = 0, int i = 1 ); // default constructor // function addIncrement definition void addIncrement() count += increment; } // end function addIncrement void print() const; // prints count and increment private: int count; const int increment; // const data member int &refCount; }; // end class Increment
Increment value( 10, 5 ); value.print(); // constructor Fig 10.5 Increment::Increment( int c, int i ) : count( c ), // initializer for non-const member increment( i ), // required initializer for const member refCount ( count ) // required initializer for ref member { cout << "Now count = " << count << ", increment = " << increment << " and refCount= " << refCount<< endl; refCount = 99; } // print count and increment values void Increment::print() const cout << "count = " << count << ", increment = " << increment << endl; Increment value( 10, 5 ); value.print(); Now count = 10, increment = 5 and refCount = 10 count = 99, increment = 5
2018年11月29日9时8分 思考: 为何const data member (例外?const static integer)与 reference data member 引用类型的数据成员必须用初始化列表操作? 从const与ref的特性出发考虑 Const数组成员的初始化?
Topics 10.1 Introduction 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 10.8 Data Abstraction and Information Hiding
10.3 Composition: Objects as Members of Classes Composition, 组合: A class has objects of other class as members. Host Object(宿主对象) vs Contained Object(被包含对象) Employees - LastName: String - FirstName: String - birthDate: Date - hireDate: Date 组合对象的初始化问题?
#include <iostream> using namespace std; class Test{ public: Test(int a){ num = a; } private: int num; }; class Test2{ Test2(int a, int b) { num = b; }; Test t; int main() { Test2 test(10, 20); return 0; } #include <iostream> using namespace std; class Test{ public: Test(int a){ num = a; } private: int num; }; class Test2{ Test2(int a, int b): t(a) { num = b; }; Test t; int main() { Test2 test(10, 20); return 0; } 使用构造函数初始化列表, 对类成员t进行初始化! error C2512: 'Test' : no appropriate default constructor available
是否存在编译错误? 缺省拷贝构造函数 #include <iostream> using namespace std; class Test{ public: Test(int a){ num = a; } private: int num; }; class Test2{ Test2(const Test &ref, int b): t(ref) { num = b; }; Test t; int main() { Test ref(10); Test2 test(ref, 20); return 0; } 是否存在编译错误? 缺省拷贝构造函数
程序解读 P407 fig10.10~14
Date.h (1 of 1)
Date.cpp (1 of 3)
Date.cpp (2 of 3)
Date.cpp (3 of 3)
Employee.cpp (1 of 2) 拷贝构造函数
Employee.cpp (2 of 2)
Outline fig10_14.cpp (1 of 2) 传递对象到宿主对象构造函数
成员对象的构造和析构顺序(Employee manager包含Date类的birthDate和hireDate) 成员对象的构造先于宿主对象; birthDate和hireDate manager 成员对象按照类定义中的声明顺序构造 (not in the order they are listed in the constructor‘s member initializer list) ; birthDate hireDate 成员对象的析构后于宿主对象。 manager hireDate birthDate
10.3 Composition: Objects as Members of Classes birth构造: Date object constructor for date 7/24/1949 Hire构造: Date object constructor for date 3/12/1988 birthDate缺省拷贝构造: 无输出 hireDate缺省拷贝构造: 无输出 manager构造: Employee object constructor: Bob Blue lastDayOff构造: Date object constructor for date 1/1/1994 lastDayOff析构: Date object destructor for date 1/1/1994 manager析构: Employee object destructor: Blue, Bob hireDate析构: Date object destructor for date 3/12/1988 birthDate析构: Date object destructor for date 7/24/1949 hire析构: Date object destructor for date 3/12/1988 birth析构: Date object destructor for date 7/24/1949 lastDayOff manager hireDate birthDate hire birth main栈区
10.3 Composition: Objects as Members of Classes 2018年11月29日9时8分 10.3 Composition: Objects as Members of Classes 总结 Date类没有显性提供接受一个Date类参数的构造函数。如何利用初始化器进行初始化? 若无缺省构造函数,则必须使用初始化列表(思考原因) 若有缺省构造函数,此时如果成员对象没有显式通过成员初始化列表中初始化,则自动隐含调用缺省构造函数(default constructor). 性能方面?(避免双重初始化) 构造函数与析构函数的调用顺序 类的成员对象(public or private) 把成员对象声明为public不会影响成员对象的封装性 双重初始化: 1、当成员对象的默认构造函数被调用时,另一次实在构造函数体重调用设置函数
Topics 10.1 Introduction 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 10.8 Data Abstraction and Information Hiding
10.4 friend Functions and friend Classes 2018年11月29日9时8分 10.4 friend Functions and friend Classes 类外部的函数和类,如何访问该类的非公有成员? Friend Function(友元函数) & Friend Class(友元类) 为什么要使用友元函数 两个类要共享数据的时候 运算符重载的某些场合需要使用友元 意义: 使用友元函数可提高性能 用友元函数可以访问两个或多个类的私有数据,较其它方法使人们更容易理解程序的逻辑关系 缺点:破环了类的封装机制 以complex的add为例 少了函数传递参数,友元函数可以直接访问
10.4 friend Functions and friend Classes --- 友元函数 To declare a function as a friend of a class, precede the function prototype in the class definition with keyword friend. 在类的作用域之外定义,在类中任何地方声明 class Count { friend void setX( Count &, int ); public: Count( ) : x( 0 ) // initialize x to 0 // empty body } void print() const { cout << x << endl; } private: int x; }; void setX( Count &c, int val ) { c.x = val; } int main() { Count counter; counter.print(); setX( counter, 8 ); return 0; } counter.setX(counter, 5); Count授权setX为其友元 8
10.4 friend Functions and friend Classes --- 友元类 To declare all member functions of ClassTwo as friends of class ClassOne, place a declaration of the form friend class ClassTwo; in the definition of class ClassOne.
10.4 friend Functions and friend Classes --- 友元类 class ClassOne { friend class ClassTwo; // friend declaration int x, int y; }; class ClassTwo{ public: void setX(ClassOne &one, int x){ one.x = x; } void setY(ClassOne &one, int y){ one.y = y; } void printClassOne(ClassOne &one){ cout << "ClassOne.x = " << one.x << ", ClassOne.y = " << one.y << endl; } int main() { ClassOne one; ClassTwo two; two.setX(one, 10); two.setY(one, 20); two.printClassOne(one); return 0; } ClassOne授权ClassTwo为其友元 ClassOne.x = 10, ClassOne.y = 20 error C2248: cannot access private member declared in class 'ClassOne'
总结-使用友元函数前应注意: 类的友元函数在类作用域之外定义,但可以访问类的私有和保护成员 尽管类定义中有友元函数原型,友元函数仍然不是成员函数 由于友元函数不是任何类的成员函数,所以不能用句柄(对象)加点操作符来调用 public, private, protected成员访问符与友员关系的声明无关,因此友元关系声明可在类定义的任何位置,习惯上在类定义的开始位置 友元关系是指定的,不是获取的,如果让类B成为类A的友元类,类A必须显式声明类B为自己的友元类 友元关系不满足对称性和传递性 如果一个友元函数想与两个或更多类成为友元关系,在每个类中都必须声明为友元函数
输出结果: #include <iostream> using namespace std; class Virus; // class Bacteria { private: int life; public: Bacteria(int x) { life=x; } friend void Check(Bacteria, Virus); }; class Virus { private: int life; public: Virus(int x) : life(x) {} friend void Check(Bacteria, Virus); }; void Check (Bacteria b, Virus v) //友元函数的定义 { if (b.life==1 && v.life==1) cout<<"/nThere is big trouble."; else if (b.life == 1) cout<< "/nWe need antibiotics."; else if (v.life==1) cout<<"/nWe need interferon."; } 2018年11月29日9时8分 int main() { Bacteria fever(1); Virus cholera(0); Check(fever, cholera); return 0; } 输出结果: We need antibiotics.
Topics 10.1 Introduction 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 10.8 Data Abstraction and Information Hiding
10.5 Using the this Pointer Test成员函数 void Test::printX( ) void Test::printY( ) const printX函数如何知道应访问哪个内存空间的x? test1.printX() test1 int x, y test2 int x, y
printX( Test * const this ) printY( const Test * const this ) 10.5 Using the this Pointer Test成员函数 void Test::printX( ) void Test::printY( ) const This指针的类型取决于成员函数是否为const printX( Test * const this ) printY( const Test * const this ) test1.printX() test1.printX( &test1 ) 特例: static member function Why? P415 10. 17 程序解读
10.5 Using the this Pointer Implicitly 隐含this指针调用 Explicitly 显式调用 this->x (*this).x Another use… .运算符比*运算符优先级高
10.5 Using the this Pointer 程序解读 P416 10.18-20 Cascaded Function Calls 级联函数调用 t.setHour( 18 ).setMinute( 30 ).setSecond( 22 ); t.setMinute( 30 ).setSecond( 22 ); t.setSecond( 22 ); P416 10.18-20 程序解读
class Time { public: Time( int = 0, int = 0, int = 0 ); Time &setTime( int, int, int ); Time &setHour( int ); Time &setMinute( int ); Time &setSecond( int ); void print() const; private: int hour; int minute; int second; }; class Time2 { public: Time2( int = 0, int = 0, int = 0 ); Time2 setTime( int, int, int ); Time2 setHour( int ); Time2 setMinute( int ); Time2 setSecond( int ); void print() const; private: int hour; int minute; int second; };
#include "Time.h" // Time class definition Time::Time( int hr, int min, int sec ) { setTime( hr, min, sec ); } Time &Time::setTime( int h, int m, int s ) setHour( h ); setMinute( m ); setSecond( s ); return *this; Time &Time::setHour( int h ) hour = ( h >= 0 && h < 24 ) ? h : 0; Time &Time::setMinute( int m ) { minute = ( m >= 0 && m < 60 ) ? m : 0; return *this; } Time &Time::setSecond( int s ) second = ( s >= 0 && s < 60 ) ? s : 0; void Time::print() const cout << setfill( '0' ) << setw( 2 ) << hour << ":" << setw( 2 ) << minute << ":" << setw( 2 ) << second <<endl;
#include "Time.h" #include "Time2.h" int main() { Time t; Time2 t2; t.setTime( 20, 20, 20 ).setHour( 18 ).setMinute( 30 ).setSecond( 22 ); cout << "t: "; t.print(); t2.setTime( 20, 20, 20 ).setHour( 18 ).setMinute( 30 ).setSecond( 22 ); cout << "\nt2: "; t2.print(); return 0; }
Topics 10.1 Introduction 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 10.8 Data Abstraction and Information Hiding
10.6 Dynamic Memory Management with Operators new and delete Motivation char sentence[ 1000 ]; Fixed-size array. 1000? Dynamic memory management 根据需求分配(allocate)、释放(deallocate)内存 new / delete operator
10.6 Dynamic Memory Management with Operators new and delete use the new operator to dynamically allocate the exact amount of memory required at execution time in the free store (sometimes called the heap) 用new运算符在执行期间从堆中申请动态内存空间 return the memory to the free store by using the delete operator to deallocate (i.e., release) the memory, which can then be reused by future new operations 通过delete关键字释放内存空间 memory leak (内存泄露)
10.6 Dynamic Memory Management with Operators new and delete allocates storage of the proper size for an object calls the default constructor to initialize the object returns a pointer of the type specified to the right of the new operator delete 对象: calls the destructor for the object to which pointer points deallocates the memory associated with the object
10.6 Dynamic Memory Management with Operators new and delete 2018年11月29日9时8分 10.6 Dynamic Memory Management with Operators new and delete 使用堆空间的理由 直到运行时才能知道需要多少对象空间; 不知道对象的生存期到底有多长; 直到运行时才知道一个对象需要多少内存空间 申请时不能调用带参数的构造函数 [1]从静态存 储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。 [2]在栈上创建。 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 [3]从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在 何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内 存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
10.6 Dynamic Memory Management with Operators new and delete 2018年11月29日9时8分 10.6 Dynamic Memory Management with Operators new and delete (1) 基本数据类型 double *ptr = new double; cout << *ptr << endl; delete ptr; ( 3.14159 ); 初始值 可增加对数组的操作 -6.27744e+066 3.14159
10.6 Dynamic Memory Management with Operators new and delete (2) 类对象 class Time{ public: Time(){ cout << "Time constructor called.\n"; } ~Time(){ cout << "Time destructor called.\n"; } }; int main() { Time *timePtr = new Time; delete timePtr; return 0; } Time constructor called. Time destructor called.
10.6 Dynamic Memory Management with Operators new and delete (2) 类对象 class Time2{ public: Time2( int, int, int); ~Time2(); }; int main() { Time2 *timePtr = new Time2( 12, 45, 0 ); delete timePtr; return 0; } 构造函数参数列表
10.6 Dynamic Memory Management with Operators new and delete (3) 数组 – 基本数据类型 int size = 10; int *gradesArray = new int[ size ]; delete [ ] gradesArray; 注意与Fixed size数组的区别: Constant integral expression vs Any integral expression
10.6 Dynamic Memory Management with Operators new and delete (3) 数组 – 类对象 Time *timePtr = new Time[5]; delete [ ] timePtr; Time constructor called. Time destructor called.
10.6 Dynamic Memory Management with Operators new and delete (3) 数组 – 类对象 Time *timePtr = new Time[5]; delete timePtr; Time constructor called. Time destructor called.
10.6 Dynamic Memory Management with Operators new and delete 2018年11月29日9时8分 10.6 Dynamic Memory Management with Operators new and delete (3) 数组 – 类对象 when allocating an array of objects dynamically, the programmer cannot pass arguments to each object's constructor. 无法传递构造函数参数 Instead, each object in the array is initialized by its default constructor. 只能调用默认的构造函数 申请时不能调用带参数的构造函数
Topics 10.1 Introduction 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 10.8 Data Abstraction and Information Hiding
2018年11月29日9时8分 10.7 static Class Members Static data member: an important exception to the rule that each object of a class has its own copy of all the data members of the class 仅有变量的一份副本供类的所有对象共享 Static member function 可以看做是类的服务,同对象无关 1、静态数据成员要在类外定义。 2、类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数,如果一定要访问成员变量,可以在参数中传入对象,通过对象可以访问类的私用和公有成员。 const修饰符用于表示函数不能修改成员变量的值,该函数必须是含有this指针的类成员函数,函数调用方式为thiscall 静态成员的空间分配并不在类的构造函数中完成,也不会在类的析构函数里完成空间回收 静态类型成员必须要在类的实现文件中定义(头文件?Main函数文件?) 静态数据成员在程序已开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化
10.7 static Class Members --- data member 2018年11月29日9时8分 10.7 static Class Members --- data member 静态数据成员可以至多初始化一次: 显式初始化 静态常量整数成员可在类定义中初始化(长整形/短整形/枚举) const static int i = 0; //ok 其它情况必须在类外部定义和初始化。 static int i = 0; //error const数组成员的初始化?* 声明为const static数组,并在外部初始化 若没有显式初始化 静态基本类型的数据成员缺省初始化为0 静态抽象数据类型(类对象)的数据成员默认调用缺省构造函数 静态成员的空间分配并不在类的构造函数中完成,也不会在类的析构函数里完成空间回收 Review 在编译时就完成了内存的分配
num1 = 0 num2 = 10 error LNK2001: unresolved external symbol #include <iostream> using namespace std; class Class1{ public: Class1() { cout << "num1 = "<< num1 << endl; } void print() { cout << "num2 = "<< num2 << endl; } private: static int num1; const static int num2 = 10; }; class Class2{ Class2(){ } static Class1 obj1; Class1 Class2::obj1; int main() { Class2 obj2; obj2.obj1.print(); return 0; } int Class1::num1; int Class1::num1=20; num1 = 0 num2 = 10 error LNK2001: unresolved external symbol "private: static int Class1::num1"
num1 = 20 num2 = 10 #include <iostream> using namespace std; class Class1{ public: Class1(int n) { cout << "num1 = "<< num1 << endl; } void print() { cout << "num2 = "<< num2 << endl; } private: static int num1; const static int num2 = 10; }; class Class2{ Class2(){ } static Class1 obj1; int main() { Class2 obj2; obj2.obj1.print(); return 0; } num1 = 20 num2 = 10 int Class1::num1=20; Class1 Class2::obj1 = Class1(30);//or obj1(30) Class1 Class2::obj1;
10.7 static Class Members --- member function 静态成员函数 可直接通过类名+::访问 没有this指针(为什么?) static 成员函数独立于对象存在。而this指针必须指向类的具体的对象 不能加const(为什么?) const修饰符用于表示函数不能修改成员变量的值,该函数必须是含有this指针
10.7 static Class Members --- member function #include <iostream> using namespace std; class Obj { public: Obj( int n ){ num = n; } static void print(){ cout << num << endl; } static void display(){ cout << count << endl; } private: int num; static int count; }; Int Obj::count = 1; int main( ) Obj obj; Obj::print(); obj.display(); return 0; } 静态成员函数 可通过对象访问(public),也可直接通过类名+::访问 不能访问非静态数据成员,也不能调用非静态成员函数 10.21-23 程序解读 error C2597: illegal reference to non-static member 'Obj::num'
Topics 10.1 Introduction 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 10.8 Data Abstraction and Information Hiding
10.8 Data Abstraction and Information Hiding-软件工程思想 2018年11月29日9时8分 10.8 Data Abstraction and Information Hiding-软件工程思想 A class normally hides its implementation details from its clients. This is called information hiding (信息隐藏). The client knows only that when data items are placed in the stack, they will be recalled in last-in, first-out order. The client cares about what functionality a stack offers, not about how that functionality is implemented. This concept is referred to as data abstraction(数据抽象). (软件升级,public接口) An abstract data type actually captures two notions: A data representation and the operations that can be performed on those data. (数据表达+执行操作,如复数、大整数类)
Summary 常量对象和常成员函数 对象的组合 友元函数和友元类 this指针(隐含参数,作用:级联调用) 动态内存分配 静态类成员
Homework! 实验必选题目(交实验报告): 10.7,10.8,10.9,10.10
2018年11月29日9时8分 7.6 Case Study: Class GradeBook Using an Array to Store Grades(利用数组存放成绩) 类的const类型成员 类定义中不能进行初始化,因为头文件中类的定义只是一个声明,并没有分配真正空间,因此变量是不存在的,因此是不能赋值的。 const 定义的变量必须赋值 解决办法: 在构造函数后的参数初始化列表中初始化 将const变量同时声明为 static 类型进行初始化 类的static类型成员 采用静态数据成员来实现同一个类的对象共享数据 静态数据的成员必须要在类的外部给出它的定义,定义时可以初始化,在类内部声明 类的const static类型成员 静态成员的空间分配并不在类的构造函数中完成,也不会在类的析构函数里完成空间回收
//GradeBook.h #include <string> using std::string; class GradeBook { public: const int SIZE; static int GradeBookNumber; const static int number = 10; GradeBook( string, const int [] ); void setCourseName( string ); // function to set the course name string getCourseName(); // function to retrieve the course name void displayMessage(); // display a welcome message private: string courseName; // course name for this grade book int grades[ 10 ]; // array of student grades };
成员初始化表 // GradeBook.cpp 2018年11月29日9时8分 // GradeBook.cpp #include "GradeBook.h" // GradeBook class definition int GradeBook:: GradeBookNumber = 0; GradeBook::GradeBook( string name, const int gradesArray[] ):SIZE(10) { setCourseName( name ); // initialize courseName for ( int grade = 0; grade < SIZE; grade++ ) { grades[ grade ] = gradesArray[ grade ]; cout<<grades[ grade ]<<'\t'; } } // end GradeBook constructor void GradeBook::displayMessage() // this statement calls getCourseName to get the // name of the course this GradeBook represents cout << "Welcome to the grade book for\n" << getCourseName() << "!" << endl; cout<<“static grade book number= "<<GradeBookNumber<< endl; GradeBookNumber ++; } // end function displayMessage 成员初始化表
Return void GradeBook::setCourseName( string name ) { courseName = name; // store the course name } // end function setCourseName // function to retrieve the course name string GradeBook::getCourseName() return courseName; } // end function getCourseName //MainTest.cpp #include "GradeBook.h" // GradeBook class definition int main() int gradesArray[10] = { 87, 68, 94, 100, 83, 78, 85, 91, 76, 87 }; GradeBook myGradeBook1("Wellcom to C Programming", gradesArray ); myGradeBook1.displayMessage(); GradeBook myGradeBook2("Wellcom to C++ Programming", gradesArray ); myGradeBook2.displayMessage(); return 0; } // end main Return
实验2中的问题 类声明并没有分配内存空间,且static变量独立于类对象的存储空间