时间:2014.04.04
地点:基地二楼
--------------------------------------------------------------------
一、静态方法
和类的静态数据成员一样,有时整个类也只要一份即可。这个方法应该属于类,而不转属于某个对象。这些方法不会访问特定对象的信息。比如:
class MyClass { //...... protected: static string DoubleToString(double val); static double StringToDouble(const string& str); //...... };但有一个问题,类的常量数据成员常设置为静态的,但这里的静态方法不允许声明为const方法。而且方法的实现无需重复static关键字了。静态方法因为不属于特定对象,于是没有this指针,当用某个特定对象调用静态方法时,静态方法不会访问这个对象的非静态数据成员。实际上静态方法就像一个普通函数,区别只在于这个方法还可以访问类的private和protected静态数据成员。类中的任何方法都可以像调用普通函数那样调用类的静态方法。如果想在类外调用静态方法,加上作用域解析运算符限定即可。总的一句说来,静态方法更像一个普通函数,只是属于这个类而已再加上一点作用域修饰符限制。
--------------------------------------------------------------------
二、const方法
常量对象时值不能改变的对象,即如果有一个对象是常量对象,我们不能试图去通过一些操作来改变里面数据成员的值。这样在使用常量对象,包括常量对象的引用和常量对象的指针,编译器都不允许调用对象的一般方法,除非该方法能保证不会改变任何数据成员,那么方法是如何承诺不会改变对象的数据的呢?它得签署协议,发表声明,说自己是const的才行,即用const关键字标记自己。比如:
class MyClass { public: //...... double get_value() const; string get_string() const; //...... }这里的const和刚刚上面的static就不一样了,const属于方法原型的一部分,方法的定义必须与之匹配
double MyClass::get_value() const { return value_; } string MyClass::get_string() const { return string_; }将方法标记为const意味着方法发誓承诺不会在方法内改变对象内部的值。那么前面说的静态方法为什么不能声明为const呢?这显然是多余的,静态方法没有类的实例,都不可能接触到类的内部数据成员,不可能改变内部值,const的机制时将方法内用到数据成员的标记为const,表示发誓此生不能被利用来修改数据成员,只能查看和访问。否则编译不通过。
要说明的是非const对象是可以调用const方法和非const方法的,只是const对象只能调用const方法。要形成这样一种习惯,将不修改对象的所有方法都声明为const。但const对象也会被销毁,析构函数会被调用,我们不应该将析构函数声明为cosnt。
还有一个问题,有时你编写的代码逻辑上是const的,要碰巧我们需要改变对象的数据成语,这个逻辑上const的只是逻辑上的,是假的,我们想在这里const里改变数据成员确定是可控的,不会带来任何负面影响,而且恰巧是完成任务所必须的。例如,假设你想获得类中获取数据被读取的频率,我们可以在类中设置一个计数器,每次调用get函数时对计数器加1,一方面我们确实要让get函数是const的,因为并不想通过它来修改正式的类数据成员,另一方面,我们确实想改变类的一个数据成员,尽管它听起来没那么正式。一种解决这种矛盾的办法就是将变量设置为mutable。告诉编译器在const方法中是允许改变的。
class MyClass { //...... protected: double value_; string string_; mutable int num_access_=0; };
double MyClass::get_value() const { num_access++; return value_; } string MyClass::get_string() const { num_access++; return string_; }
--------------------------------------------------------------------
三、方法重载
类中可以编写多个构造函数,这些构造函数名称相同,只是参数数量或类型不同。C++中对于其他方法也一样,这叫做方法重载或者函数重载,编译器会根据传递的参数的不同判断具体调用哪个实例,这一步叫做重载解析。但注意C++中不允许根据方法的返回类型重载方法名称,而根据const重载方法时可以的。在存在const 和非const两个版本的类中,const对家将调用const版本,因为对于const对象而言,非const方法本身就是不可见的,而对于非const变量而言,原则上两个版本都可调用,但在存在非const版本的情况下,优先选择调用非const版本。
C++11中重载方法可被显示删除,防止调用具有特定参数的成员函数,比如:
class MyClass { public: void foo(int i); };我们可以这样调用都正确
MyClsss c; c.foo(123); c.foo(1.23);编译器会将double值1.23转换为整型值1,然后调用foo(int I)。也许这样做并不是我们所期待的,于是我们要显示删除foo()的double实例,禁止编译器这么转换。原理是:void foo(double i) 显示的有,碰到1.23时编译器会去找这个函数,而不发生隐式转换去找foo(int i)了,但foo(double i)是delete的,所以不会执行。实现如下:
class MyClass { public: void foo(int i); void foo(double d)=delete;
--------------------------------------------------------------------
四、默认参数
C++中函数原型或方法可指定默认值,要是用户指定了这些参数,默认值被忽略,否则将会使用默认值,限制是默认值的给出只能从最后边的参数开始提供连续的默认参数列表,否则编译器无法用默认值匹配遗失的参数。默认参数在构造函数中最有用。例:
class Spreadsheet { public: Spreadsheet(const SpreadsheetApplication& the_app,int in_width=kMaxWidth,int in_height=kMaxHeight); };所有参数都有默认值得构造函数等用于默认构造函数,即可在创建类对象时不指定任何参数。如果试图同时声明默认构造函数和所有参数都有默认值得构造函数编译器会报错。因为二者的等效性,当不提供任何参数数,编译器不晓得调用哪个构造函数了。
--------------------------------------------------------------------
五、内联方法
C++中提供这样一种能力,函数或者方法的调用可以不以生成代码的方法实现,就像调用独立的代码块,编译器会将方法体或者函数体直接插入到调用方法或者函数的位置,俗称函数体替换,就像我们的宏函数一样。这个过程成为内联。具有这一行为的函数或方法成为内联方法或函数。内联比使用#define宏要安全。具体实现是在方法或者函数定义前使用inline关键字将它们指定为内联的。例如:
inline double MyClass::get_value() const { num_accesses++; return value; } inline string MyClass::get_string() const { num_accesses++; return string_; }这样编译器就可选择用实际的方法体替换get_value以及get_string的调用,而不是生成代码通过函数调用。
值得注意的问题是:如果没有看到函数的定义,编译器怎么替换函数体?因此编写内联函数或者方法时,应该将定义与原型一起放在头文件中。
C++提供另外一种声明内联方法的语法,而无需使用inline关键字,而是直接将方法定义放在类的定义中。比如:
class MyClass() { public: //...... double get_value() const{ num_access++;return value_;} string get_string() const{ num_access++;return string_;} //...... };内联方法的应用受限,编译器只会内联最简单的方法以及函数,如果将编译器不想内联的方法定义为内联,编译器会自动忽略,其次,内联使得代码膨胀,在每个调用的地方都会重新生成方法体,这会增加可执行程序的大小。