2020/3/18 学习笔记

1.对象的生存期:对象从产生到结束的这段时间就是他的生存期。
在对象生存期内,对象将保持他的值,直到被更新为止。
静态生存期:这种生存期与程序的运行期相同。在文件作用域中声明的对象具有这种生存期。在函数内部声明静态生存期对象,要冠以关键字static,尽管他是局部变量、局部对象,虽然他的作用域在函数体局部范围内,但在整个程序运行期内他的数据是保有的,不会被破坏,除非赋予新值。
动态生存期:开始于程序执行到声明点时,结束于命名该标识符的作用域结束处。块作用域中声明的,没有用static修饰的对象是动态生存期的对象(习惯称局部生存期对象)。
例子:

#include <iostream>

using namespace std;

int i=1;//i为全局变量,具有静态生存期。
void other()
{
    static int a=2;
    static int b;//a、b为静态局部变量,具有全局寿命,局部可见。
    //只第一次进入函数时被初始化。
    int c=10;//c为局部变量,具有动态生存期。
    //每次进入函数时都要初始化。
    a+=2;i+=32;c+=5;
    cout<<"---OTHER---\n";
    cout<<"i:"<<i<<" a:"<<a<<" b:"<<b<<" c:"<<c<<endl;
    b=a;
}
int main()
{
    static int a;//静态局部变量,有全局寿命,局部可见。
    int b=-10;//b、c为局部变量,具有动态生存周期。
    int c=0;
    cout<<"---MAIN---\n";
    cout<<"i:"<<i<<" a:"<<a<<" b:"<<b<<" c:"<<c<<endl;
    c+=8;other();
    cout<<"---MAIN---\n";
    cout<<"i:"<<i<<" a:"<<a<<" b:"<<b<<" c:"<<c<<endl;
    i+=10;other();
    return 0;
}

在这里插入图片描述
通过例子我们知道,在other函数中的a、b具有全局寿命,局部可见,因此第一次进入函数体初始化a、b之后出函数体,第二次进入函数体时不会再次初始化a、b,而是继续使用a、b内存空间中已保存的值。这一点值得记忆。
2.静态数据成员:用关键字static声明;为该类的所有对象共享,静态数据成员具有静态生存期。必须在类外定义和初始化,用(::)来指明所属的类。
例子:

#include <iostream>

using namespace std;

class Point
{
public:
    Point(int X=0,int Y=0):x(X),y(Y)//构造函数
    {
        count++;//在构造函数中对count累加,所有对象共同维护一个count
    }
    Point(Point &p)//复制构造函数
    {
        x=p.x;y=p.y;count++;
    }
    ~Point(){count--;}//析构函数给count减1
    int getX(){return x;}
    int getY(){return y;}
    void showCount()//输出静态数据成员
    {
        cout<<"Object count="<<count<<endl;
    }

private://私有数据成员
    int x,y;
    static int count;//静态数据成员声明,用于记录点的个数。
};
int Point::count=0;//静态数据成员定义和初始化,使用类名限定

int main()//主函数
{
    Point a(4,5);//定义对象a,其构造函数会使count增1
    cout<<"Point A:"<<a.getX()<<","<<a.getY();
    a.showCount();

    Point b(a);//定义对象b,其构造函数会使count增1
    cout<<"Point B:"<<b.getX()<<","<<b.getY();
    b.showCount();
    return 0;
}

输出
从例子中可以看出,一般构造函数和析构函数中的操作是相对的。
可以在类中定义一个静态的变量,为整个类的所有对象所共享,保存共享的数据。
但值得注意的是,当没有Point类的对象被创建时,count的值为0,然而此时无法查询Point类对象的数目,尽管我们已知它为0,实际也确实存在count值为0。但是我们无法使用,因为没有创建对象。解决方案见后文。

3.类的友元:
友元是C++提供的一种破坏数据封装和数据隐藏的机制。
通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息。
可以声明友元函数和友元类。
为了确保数据的完整性,及数据封装与隐藏的原则,建议慎用友元。
4.友元函数:友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问private和protected成员。
作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择。
访问对象中的成员必须通过对象名。

#include<iostream>
#include<cmath>

using namespace std;

class Point// Point类声明
{
public: //外部接口
    Point(int x=0,int y=0):x(x),y(y){}
    int getX(){return x;}
    int getY(){return y;}
    friend float dist(Point &a,const Point &b );

private: //私有数据成员
    int x,y;
};
float dist(Point &a,const Point &b)
{
    double x=a.x-b.x;
    double y=a.y-b.y;
    return static_cast<float>(sqrt(x*x+y*y));
}

int main()
{
    Point p1(1,1),p2(4,5);
    cout<<"The distance is:";
    cout<<dist(p1,p2)<<endl;
    return 0;
}

计算两个点之间的距离我们用一个函数dist来算,这个函数我们并没有把它作为类的成员函数,而是在类外作为一个独立的全局函数,将两个点的引用作为参数传递给它。这时候遇到一个问题,x和y都是私有成员,在外部是不能直接访问的,都要用类提供的接口getX和getY来访问的,但是如果我们需要频繁调用x和y,这样会有大量时间空间开销,这都是为了对类中私有成员的隐藏和保护。
如果我们想把效率提高一点,我们可以用友元函数的方式。在类中声明为该类的友元,实际上是给dist函数一个授权,让他可以访问x坐标y坐标,但它不是成员函数,所以它要访问两个点时我们必须把两个点作为参数传过去。至于为何代码中传参数用引用而不直接传值?这又牵扯到一个效率的问题,引用传递一个别名过去效率比较高。这样float函数就可以直接访问x和y了。
当然为了防止类的私有成员被改变,我们可以在引用之前加const关键字以将其设置为只读。
5.友元类:若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。
声明语法:将友元类名在另一个类中使用friend修饰说明。
例如:

class A
{
    friend class B;
public:
    void display()
    {
        cout<<x<<endl;
    }
private:
    int x;
};

class B
{
public:
    void set(int i);
    void display();
private:
    A a;
};

void B::set(int i)
{
    a.x=i;
}

void B::display()
{
    a.display();
}

这是一个类的组合,B是组合类,A是部件类。我们想用B中set函数来设置A类对象的x值,但是按理说A中的私有成员B并没有权限直接访问,所以如果我们特别需要给部件类中私有对象直接赋值,可以考虑将B类说明为A类的友元。
但友元机制要慎用,如果赋予很多其他类友元的权限,那么该类的私有成员的安全性就有问题了。
类的友元关系是单向的:声明B类是A类的友元不等于A类是B类的友元。

发布了3 篇原创文章 · 获赞 0 · 访问量 10

猜你喜欢

转载自blog.csdn.net/qq_18839043/article/details/104948800