C++深入理解(6)------类容易被忽略的小知识点(读书笔记)

    相信大家都能比较熟练的使用C++的类了,但是在使用类的时候往往只用到其基本的使用规则,而很多小的细节却极容易被大家忽略,下面将这些小的知识点列出,提醒自己平时应用时要注意细节:

1.类内的内联方法:

    在类声明中定义的函数都是类的内联函数,根据C++的唯一性原则,函数是不能被定义为两次的,如果两个文件中都同时包含类声明的头文件,除了内联函数(拥有内部链接性,也就是说只对包含他的文件起作用,而并非全局的,外部是不会知道其已经定义过。)都会被认为是错的,所以内联函数却不会触发这种重复定义的问题,所以可以放心的使用类似返回数值等的操作。

2.关于构造函数和析构函数的调用顺序

    如果有两个类的定义,那么析构函数的调用顺序是怎么样的那?这也被常用来做笔试题。定义内部变量是放在堆栈中的,堆栈的规则是先入后出,所以优先定义的类的析构函数最后被调用。

3.如何将C++的类改为C语言的结构体

    其实结构体封装之后也是可以实现类的效果,但是结构体和类却略有不同,首先类中如果不声明方法或属性,默认是私有的,而在结构体中,默认是共有的;在类中会隐藏this指针,而在结构体中并没有this指针,所以在转化的过程中需要将this明确的声明作为类成员变量,然后在使用的过程中就可以显式的通过this指针调用本类的一些结构体。

4.类的作用域

    类的作用域指的是在类中定义的属性和方法,作用域只在类中,所以如果想调用对应的属性和方法,就必须使用该对象的值,例如student::func();除此之外,还有另一种神奇的用法,就是在定义枚举的时候,如果全局的枚举值对应的整数值相同,那么就会导致破坏其定义的唯一性,如下面

enum class WeekDay = {Mon,Tues};
enum class CardType = {One,Two};

    如果定义类似上面的,就会报错,即使在不同文件中定义,在编译的过程中也是会链接错误,所以可以通过下面的方式来定义相同值的枚举值,来表示不同的含义:

enum class WeekDay = {Mon,Tues};
enum class CardType = {One,Two};

    这中定义方式叫做作用域内枚举,可以含有不同名称的相同数值,当然class还可以用struct来代替。用法也比较简单,可以使用域作用符号来使用,如WeekDay::Mon。

5.重载运算符

    在刚接触重载运算符的时候,总是感觉很别扭的感觉,其实有一种非常好理解的方式:一般重载运算符都写为operator+(参数)的形式,而在实际的应用中必须这样:对象名+(参数),所以就可以理解为+只是该类的一个成员函数,即下面:

temp1+temp2;
就等于
temp1.operator+(temp2);

    重载运算符也有很多的注意点,详细见C++ Primer的11章。

    重载还有另外一种方式,Time operator+(double,const &Time);这个可以类似于这样:Time abc = 10 + Time2;也就是Time abc = (operator + (10,Time2)),这种二元重载的一般都是double+Time2的形式,在举一个例子方便理解:

cout << trip;            //trip为Time类型的对象,无法直接输出

//二元运算符重载,按顺序操作,即传入(cout,trip) 等于cout<<trip
void operator<<(ostream & os, const Time & t)       
{
os << t .hours << " hours, " << t.minutes << " minutes " ;
}

    这里格外说下,使用上面的重载运算符将不能执行下面的操作

cout<<trip<<"hello"<<endl;

    但是以前都是可以执行cout<<int<<double<<endl;这样的不同类型的,为什么重载之后不能输出了呢?实际情况是:

    cout<<int<<double;   //实际上表示的是(cout<<int)<<double; (cout<<int)
 
 
ostream& operator<<(ostream & os, const Time & t)       
{
os << t .hours << " hours, " << t.minutes << " minutes " ;
}
//通过这种方式,就可以使用连续的输出了

    顺便说下怎么重载一元运算符,比如-不仅可以代表减,还可以表示负的作用,所以下面讲解下怎么重载一元运算符:

Vector operator-()const;
Vector Vector::operator-() const
{
    retutn Vector (-x,-y );
}

6.关于友元

    类A中包含的友元类和友元函数表名,友元类和友元函数是A的朋友,他们可以访问A的私有成员。有人会认为破坏类的私有性,但是友元相当于类的一种扩展接口,在该书中是这么认为的。

    先说下友元函数,友元函数都是非成员函数。其基本的用法大致如下:

friend void adds()       //声明
void adds(){}            //定义

7.类的自动转化和强制类型转化

    前面说过C++定义的特定类型可以经过自动换花和强制类型转化,比如double和int之间,但是类型转化并不局限在特定的类型,我们自己定义的类也是可以经过自动转化和强制类型转化的,

Stonewt myCat;             // create a Stonewt object
myCat = 19.6 ;            // use Stonewt(double) to convert 19^ 6 to Stonewt

    如过直接这样写,那么编译器一定会报错,编译器认为不存在转化的可能,但是如果定义了下面的构造函数:

Stonewt(double lbs );     //H temprate for double-to-Stonewt conversion
Stonewt myCat;            // create a Stonewt object
myCat = 19.6 ;           // use Stonewt(double) to convert 19^ 6 to Stonewt

    赋值的过程就是可以的了。但是C++为了有些人不喜欢这种模式,增加了explicit 关键词,使用关键词修饰的构造函数是不允许进行这种隐式转化的,但是依然可以进行强制转化,即下面的

Stonewt myCat;             // create a Stonewt object
myCat = 19.6;              // not valid if Sconewt(double) is declared as explicit
mycat = Stonewt (19.6);   // ok, an explicit conversion
mycat = (Stonewt)19.6;     // ok, old form for explicit typecast:

    还有一种二义性的情况,就是我既定义了long类型的构造函数,也定义了double类型的构造函数,那么在使用下面的转化的时候就会出现问题:

Stonewt myCat;             
myCat = 19;             

    编译器会不明白应该使用double类型的还是使用long类型的构造函数去初始化这个对象,但是如果在多一个int类型的构造函数,却又是可以的了。另一个需要注意的是,如果定义了两个参数的构造函数,这个默认转化依然是不可以用的,除非第二个参数是有初值的默认参数。

    另一个问题就是既然普通类型可以转化为类的类型,那么类的类型能不能拿转化为int,double等普通的类型呢?答案是可以的,但是必须使用转换函数,转换函数的格式如下:

operator typeName();        //typeName指出具体的类型, 如int
//• 转换函数必须是类方法;
//• 转换函数不能指定返回类型;
//• 參转换函数不能有参数。
operator double()
{
    ruturn (double)this->pount;
}
    这里也要提醒一下,转换函数也是存在二义性的,如果同时定义了double和int类型,实际上在转化为cout<<对象<<endl的时候,cout就不明白应该将对象转化为int还是double。explicit 关键词在这里依然是可以使用的。
 

猜你喜欢

转载自blog.csdn.net/xiaopengshen/article/details/79506488
今日推荐