第12章 类

来源:互联网 发布:北汽股份 营销网络 编辑:程序博客网 时间:2024/05/18 00:02

         在C++中用类来定义自己的抽象数据类型。

12.1、类的定义和声明

12.1.1、类定义:扼要重述

         每个类可以没有成员,也可以定义多个成员,成员可以是数据、函数或类型别名(typedef,不过这个跟前面两个属于同一档次?是指在类里能定义类型别名这个意思?)

         构造函数初始化列表由成员名和带括号的初始值组成,跟在构造函数的形参表之后,并以冒号开头,如,Sales_item() : 成员名(值){}

         成员函数的声明必须在类内部。在类内部定义的函数默认为inline。

         将关键字const加在参数表之后,就可以将成员函数声明为常量,如double f() const;const成员不能改变其所操作的对象的数据成员。const必须同时出现在声明和定义中(即不能声明时带有const,定义时没有),否则会产生编译错误。

12.1.2、数据抽象和封装

         类背后蕴含的基本思想是数据抽象和封装。它们的两个重要优点:(1)避免类内部出现无意的、可能破坏对象状态的用户级错误;(2)随时间推移可以根据需求改变或缺陷报告来完善类实现,而无需改变用户级代码。

         每个访问标号(public、private等)出现的次数没有限制,它指定了随后的成员定义的访问级别,这个指定的访问级别持续有效,直至遇到下一个访问标号或看到类定义体的右括号为止

12.1.3、关于类定义的更多内容

         类可以定义自己的局部类型名字,typedef,该别名只针对相应的类。

         inline成员函数的定义必须在调用该函数的每个源文件中是可见的。在声明和定义处指定inline都是合法的(不需要两个都指明)。

12.1.4、类声明与类定义

         如class A;这个声明有时称为前向声明,在程序中引入类类型的A。在声明之后、定义之前,类A是一个不完全类型。不能定义该类型的对象,只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。只要类名一出现就可以认为该类已声明(这也是类数据成员可以是指向自身类型的指针或引用的原因)。

12.1.5、类对象

         定义了一个新类型(如classA{···}),不进行存储分配。定义一个A a;对象才分配。

         类的定义以分号结束。因为在类定义之后可以接一个对象定义列表(如class A{} a,aa;),但这习惯不好。

12.2、隐含的this指针

         返回*this(this为指针,返回其内容;注意跟java的this区别,java中由于只有引用,用this调用成员变量是this.成员变量名;而这边this是指针,应是this->成员变量名):对于方法中,有些方法必须返回一个引用,该引用指向执行操作的那个对象,如A& A::set(··){··; return *this;}

         在普通的非const成员函数中,this类型是一个指向类类型的const指针(即指针常量,不能修改this指针,但可以修改其指向的值)。不能从const成员函数返回指向类对象的普通引用,const成员函数只能返回*this作为一个const引用(即如果定义为const成员函数,返回值必须也声明为const,如constA& display() const{return *this;})。可以在非const对象上调用display,但不能对display后面调用非const成员函数。

         基于成员函数是否为const,可以重载一个成员函数;基于指针形参是否指向const,可以重载一个函数。const对象只能使用const成员,非const对象可以使用任一成员。

         可变数据成员(永远都不能为const,甚至当它是const对象的成员时也一样):const成员函数可以改变mutable成员。将成员声明前加mutable可以在const成员函数中对其进行改变。

12.3、类作用域

         成员函数在类外定义,要用完全限定名,如类名::方法。

         如果函数返回类型使用由类定义的类型,则必须使用完全限定名,如inline 类名::类中定义的类型类名::方法{}。

         类作用域中名字查找:(1)在使用改名字的块中查找名字的声明。只考虑在该项使用之前声明的名字;(2)如果找不到该名字,则在包围的作用域中查找。都是小范围到大范围查找的思想。函数作用域à类作用域à外围作用域。

         全局对象被屏蔽,但通过全局作用域确定操作符来限定名字(如::变量名),仍然可以使用它。类成员被屏蔽,也可以用类名或显示使用this指针来使用它。

12.4、构造函数

         构造函数的名字与类的名字相同,并且不能指定返回类型。

12.4.1、构造函数初始化式

         构造函数分两个阶段执行:(1)初始化阶段;(2)普通的计算阶段。计算阶段由构造函数函数体中的所有语句组成。类类型的数据成员总是在初始化阶段初始化(调用其默认构造函数,如果没有默认构造函数则编译不通过,需要初始化列表解决)。因此在构造体中给类类型赋值只能算是值覆盖。

         静态成员必须要在外面初始化,如在全局作用域:int C::j = 1;

         内置或复合类型的成员的初始化值依赖于对象的作用域(对象定义在main中也不会初始化):在局部作用域中,这些成员不被初始化,而在全局作用域中它们被初始化为0。如果用A a = A();或用new,局部也不会初始化···

         有些成员必须在构造函数初始化列表中进行初始化,对于这些成员,在构造函数函数体中对它们赋值不起作用。没有默认构造函数的类类型的成员,以及const或引用类型的成员都必须在构造函数初始化列表中进行初始化。而内置类型的成员则无关紧要。

         成员被初始化的次序就是定义成员的次序。好习惯:使用构造函数的形参进行初始化,而不是使用对象的数据成员初始化其他成员,防止顺序搞错。

         初始化式可以使任意表达式。

12.4.2、默认实参与构造函数

         如构造函数A(conststd::string &a=””):成员变量名(a){}。

12.4.3、默认构造函数

         不能用:类名对象变量名();来借助默认构造函数生成对象,这被编译器解释成了一个函数声明,正确的是:类名 对象变量名;或者:类名 对象变量名 = 类名();

12.4.4、隐式类类型转换

         可以用单个实参来调用的构造函数(多个实参则不行)定义了从形参类型到该类类型的一个隐式转换。比如一个构造函数含有一个int实参。有一个方法f(A a){},此时调用f(1)是可以的,实参1被隐式转换为类A了(这缺点之一是搞不好就是f方法中又调用a.f()这样,会导致无限循环)。(但这个隐式转换的类A是一个临时对象,方法一结束,就不能再访问它)。可以通过将构造函数声明为explicit(它只能用于类内部的构造函数声明上),来防止在需要隐式转换的上下文中使用构造函数

12.4.5、类成员的显式初始化

         如:structData{int ival; char *ptr;}; Data val2 = {1024,”hello”};可以这样初始化,但是顺序要注意,得先int再string。

12.5、友元

         如classA{friend class B;};此时B就可以直接引用A的私有成员。

         当我们将成员函数声明为友元,函数名必须用该函数所属的类名字加以限定。如class A{friend voidB::f();}

         友元函数可以在类的内部定义,该函数的作用域扩展到包围该类定义的作用域。如classA{friend class B;friend void f(){}};则在同一文件中的class Z{Y *e; void g(){return::f();}};

         类应该将重载函数集中每一个希望设为友元的函数都声明为友元,没必要的则不用。

12.6、static类成员

         使用static成员而不是全局对象的优点:(1)static成员名字是在类的作用域中,可以避免与其他类的成员或全局对象名字冲突;(2)可以实施封装。static成员可以是私有成员,而全局对象不可以;(3)通过阅读程序可以看出static成员与特定类关联,阅读较清晰。

         static成员函数不能被声明为const(因为将成员函数声明为const就是承诺不会修改该函数所属的对象(但static不是任何对象的组成部分))。static成员函数也不能被声明为虚函数。

         static成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。

         static关键字只能用于类定义体内部的声明中,定义不能标示为static

         只要初始化式是一个常量表达式,整型const static数据成员就可以在类的定义体中进行初始化。如static const int period = 30;虽然这个在类中,但该数据成员仍必须在类的定义体外进行定义(VS2010貌似就在内部定义,外部不定义也可以···)。

         static数据成员的类型可以是该成员所属的类类型(因为只有一份,所以计算所需空间可以,如果不是static,则如class A{A a;}根本无法计算分配空间),非static成员被限定声明为其自身类对象的指针或引用。
原创粉丝点击