C++重点知识回顾三

目录

1.类成员的存储

2. const 修饰类

2.1 const 修饰数据成员

2.2 const 修饰函数成员

2.3 const 修饰对象

2.4 小结

3. static 修饰类

3.1 static 修饰数据成员

3.2 static 修饰函数成员

3.3 static const 类型

4.指向类成员的指针

4.1 指向类数据成员的指针

4.2 指向类函数成员的指针

4.3 指向类静态成员的指针

4.4 应用

4.4.1 更加统一的接口

4.4.2 更加隐蔽的接口


1.类成员的存储

        一个对象所占的空间大小只取决于该对象中数据成员所占的空间,而与成员函数无关。类成员函数作为对象的公用函数代码,只用一段空间来存储。

        所有的对象都调用共用的函数代码段,为了保证访问的是调用对象的成员,C++设置了this指针,对象在调用公用函数时,会将对象的指针作为隐含参数传入其内,从而保证了访问的成员,属于调用者

注意事项:

1.无论成员函数在类内定义还是在类外定义,成员函数的代码段都是用同一种方式存储。

2.应当说明,常说的“某某对象的成员函数”是从逻辑角度而言的,而成员函数的存储方式,是从物理角度而言的,二者并不矛盾。

2. const 修饰类

2.1 const 修饰数据成员

        const 修饰的数据成员,称为常数据成员,可以被普通成员函数和常成员函数使用,不可以更改。

        必须要进行初始化!可以在类中定义时初始化(不推荐),或者在初始化列表中初始化(这是在类对象生成前唯一一次改变const数据成员的值的机会)

2.2 const 修饰函数成员

        const修饰函数放在,声明之后,实现体之前。可以构成重载。

void dis() const

        const 函数成员,它承诺在本函数内部不会修改类内的数据成员,为此,它只能调用承诺不会改变成员的其他 const 成员函数,而不能调用其它非 const 成员函数。

2.3 const 修饰对象

        const 修饰对象,保证在对象层面,不会修改数据成员,所以 const 对象,只能调用 const 成员函数。 不同编译器可能会要求必须自实现构造器,因为若采用默认的话,const 对象中的成员,再无初始化的机会。

2.4 小结

1.const 修饰函数,在声明之后,实现体之前。

2.const 函数只能调用 const 函数。非 const 函数可以调用 const 函数。

3.如果 const 函数构成函数重载,const 对象只能调用 const 函数,非 const 对象优先调用非 const 函数。

4.类外定义的 const 成员函数,在声明和定义处都需要 const 修饰符。

3. static 修饰类

        C++扩展了 static 在类中的语义,用于实现在同一个类,多个对象间的数据共享,协调行为的目的

        静态变量有全局变量的优势,又不会像全局变量一样被滥用。这一类变量,既可以通过类来管理,也可以通过类的静态函数来管理。

        类的静态成员,属于类,也属于对象,但终归属于类

3.1 static 修饰数据成员

声明static 数据类型 成员变量;类的内部
初始化数据类型 类名::静态数据成员 = 初值类的外部
调用

类名::静态数据成员

类对象.静态数据成员

既可以通过对象,也可以通过类访问

        静态成员变量,实现了同族类对象间的信息共享。   

        static 修饰数据成员,需要初始化,不可以类内初始化,在类外初始化时,需要类名空间,不需要static修饰符。类的声明与实现分开时,初始化在.cpp文件中(若放在.h中会出现重定义)。

        static 声明的数据成员不占用类对象的大小,存储在data段的rw段。可以通过类名访问(无对象时亦可),也可以通过对象访问(仍受访问权限控制)。

3.2 static 修饰函数成员

        为了管理静态成员,C++提供了静态函数,以对外提供接口。并且静态函数只能访问静态成员。

声明static 函数声明;类的内部
调用

类名::函数调用

类对象.函数调用

        静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装。static 修饰成员函数,仅出现在声明处,不可以在定义处。静态成员函数只能访问静态数据成员,因为:非静态成员函数,在调用时this指针被当作参数传入,而静态成员函数属于类,不属于对象,没有this指针

#include <iostream>
using namespace std;

class Server
{
public:
    Server(char name)
        :_name(name){}

    void serverOne()
    {
        if(_openFlag && _lastServedIdx < _turnCount)
        {
            _lastServedIdx++;
            cout<<"Server: "<<_name<<" is serving "<<_lastServedIdx<<endl;
        }
        if(_lastServedIdx >= _turnCount)
                _openFlag = false;
    }
    static int getTurn()
    {
        ++_turnCount;
        return _turnCount;
    }
    static bool stillOpen()
    {
        return _openFlag;
    }
private:
    char _name;
    static int _turnCount;
    static int _lastServedIdx;
    static bool _openFlag;
};

int Server::_turnCount = 0;
int Server::_lastServedIdx = 0;
bool Server::_openFlag = true;

int main()
{
    Server s1('A'),s2('B');
    int num;
    cout<<"How many in your groups"<<endl;
    cin>>num;
    cout<<"your turns are"<<endl;
    for(int i = 0; i<num; i++ )
    {
        cout<<Server::getTurn()<<endl;
    }
    do
    {
        s1.serverOne();
        s2.serverOne();
    }while(Server::stillOpen());
    cout<<"now closing server"<<endl;
    return 0;
}

3.3 static const 类型

        如果一个类的成员,既要实现共享,又要实现不可改变,那就用 static const 组合模式来修饰。修饰数据成员,必须要类内初始化。如下例

#include <iostream>
using namespace std;
class A
{
public:
    const static int dis()
    {
        cout<<i<<endl;
        return i;
    }
private:
    const static int i = 100;
};

int main()
{
    A::dis();
    return 0;
}

4.指向类成员的指针

4.1 指向类数据成员的指针

        指向类数据成员的指针,是类层面的指针,而不是对象层面的指针。

        指向非静态数据成员的指针定义时必须和类相关联,在使用时必须和具体的对象相关联

        由于类不是运行时存在的对象。因此,在使用这类指针时,首先需要指定一个类的对象,然后,通过对象来引用指针所指向的成员。

        指向类成员的指针,本质存放的不是地址,存放的是偏移量。

声明<数据类型> <类名> :: *<指针名>
初始化<数据类型> <类名> :: *<指针名>    [= &<类名>::<非静态数据成员>]
解引用

<类对象名>.*<指向非静态数据成员的指针>

<类对象指针>->*<指向非静态数据成员的指针>

#include <iostream>
using namespace std;

class Student
{
public:
    Student(string n, int nu):name(n),num(nu){}
    string name;
    int num;
};

int main()
{
    Student s1("zhangsan",23);
    Student s2("lisi",25);
    // string *ps = &s.name;// 跟封装相违背
    // cout<<*ps<<endl;
    string Student::*pn = &Student::name;
    cout<<s1.*pn<<endl;
    cout<<s2.*pn<<endl;

    Student *ps = new Student("zhaoliu",35);
    cout<<ps->*pn<<endl;
    return 0;
}

4.2 指向类函数成员的指针

        定义一个指向非静态函数成员的指针必须在三个方面与其指向的成员函数保持一致;参数列表要相同、返回类型要相同、所属的类名要相同

        由于类不是运行时存在的对象。因此,在使用这类指针时,需要首先指定类的一个对象,然后,通过对象来引用指针所指向的成员。

声明<数据类型> (<类名> :: *<指针名>) (<参数列表>)
初始化<数据类型> (<类名> :: *<指针名>) (<参数列表>)    [= &<类名>::<非静态函数成员>]
解引用

(<类对象名>.*<指向非静态函数成员的指针>) (<参数列表>)

(<类对象指针>->*<指向非静态函数成员的指针>) (<参数列表>)

#include <iostream>
using namespace std;

class Student
{
public:
    Student(string n, int nu):name(n),num(nu){}
    void dis()
    {
        cout<<"name:"<<name<<" num:"<<num<<endl;
    }
private:
    string name;
    int num;
};

int main()
{
    Student s1("zhangsan",23);
    Student s2("lisi",25);
    Student *ps = new Student("zhaoliu",35);

    void (Student::*pf)() = &Student::dis;
    (s1.*pf)();
    (s2.*pf)();
    (ps->*pf)();
    return 0;
}

4.3 指向类静态成员的指针

        指向静态成员的指针的定义和使用与普通指针相同,在定义时无须和类相关联,在使用时也无需和具体的对象相关联。

#include <iostream>
using namespace std;

class A
{
public:
    static void dis();
    static int data;
};

void A::dis()
{
    cout<<data<<endl;
}

int A::data = 100;

int main()
{
    int *pa = &A::data;
    cout<<*pa<<endl;

    void (*pf)() = A::dis;
    pf();
    return 0;
}

4.4 应用

4.4.1 更加统一的接口

#include <iostream>
using namespace std;

struct Point
{
    int plus(int x, int y)
    {
        return x+y;
    }
    int minus(int x, int y)
    {
        return x-y;
    }
    int mul(int x, int y)
    {
        return x*y;
    }
    int div(int x, int y)
    {
        return x/y;
    }
};

int oper(Point & point, int (Point::*pf)(int x, int y), int x, int y)
{
    return (point.*pf)(x,y);
}

typedef int (Point::*PF)(int x, int y);

int main()
{
    Point pt;
    PF pfun = &Point::plus;
    cout<<oper(pt,pfun,4,5);
    return 0;
}

4.4.2 更加隐蔽的接口

#include <iostream>
using namespace std;

class Game
{
public:
    Game()
    {
        pf[0] = &Game::f;
        pf[1] = &Game::g;
        pf[2] = &Game::h;
        pf[3] = &Game::i;
    }

    void select(int i)
    {
        if(i>=0&&i<=3)
            (this->*pf[i])(i);
    }
private:
    void f(int idx){cout<<"void f(int idx)"<<idx<<endl;}
    void g(int idx){cout<<"void g(int idx)"<<idx<<endl;}
    void h(int idx){cout<<"void h(int idx)"<<idx<<endl;}
    void i(int idx){cout<<"void i(int idx)"<<idx<<endl;}

    enum{
        nc = 4
    };

    void (Game::*pf[nc])(int idx);
};

int main()
{
    Game g;
    g.select(1);
    g.select(2);
    g.select(3);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值