C++(2)(数据的共享与保护)

作用域:

1.局部作用域:

       形参列表中形参的作用域:从声明处开始,到整个函数体结束为止(也就是右大括号})函数体内声明的变量同理,也是从从声明处开始,到整个函数体结束为止。

-->具有局部作用域的变量成为局部变量

2.类作用域:

       类可以被看成是一组有名成员的集合,类X的成员m具有类作用域,
       对m的访问方式有如下3种:
       (1)如果在X的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以直接访问成员m。也就是说m在这样的函数中都起作用。

class X {
public:
    int m; // 类的成员变量

    void memberFunction() {
        m = 10; // 直接访问成员变量m
        // 在这里,m引用的是类的成员变量,因为没有同名的局部变量
    }

    void anotherFunction() {
        int m; // 局部变量m
        m = 20; // 这里的m引用的是局部变量
        // 如果要访问类的成员变量m,需要使用this指针
        this->m = 30; // 使用this指针来明确引用类的成员变量
    }
};

       在memberFunction中,由于没有声明同名的局部变量,可以直接使用m来引用类的成员变量。而在anotherFunction中,由于存在一个同名的局部变量m,因此直接使用m会引用局部变量。
如果要引用类的成员变量,需要使用this->m来明确指出(this指针是一个指向当前对象的指针,它允许你访问该对象的成员变量和成员函数。在非静态成员函数中,this指针是隐含存在的,即使你没有显式地使用它。)。
    (2)通过表达式x. m或者X::m。这正是程序中访问对象成员的最基本方法。X.:m的方式一般用于访问类的静态成员。
    (3)通过ptr一>m这样的表达式,其中 ptr为指向X类的一个对象的指针。

3.命名空间作用域

 为了消除不同模块的类和函数之间由于重名引发的错误,所以创造了命名空间。实际上就是为了区分:成都的解放路和南京的解放路,消除歧义。

 命名空间语法形式:

 namespace命名空间名{
 命名空间内的各种声明(函数声明、类声明、……)
 }

引用其他命名空间类名或函数名:命名空间名::标识符名

someNs :: SomeClass objl ;
//声明一个SomeNs : : someclass型的对象obj1

使用命名空间:两种方法

1.using命名空间名::标识符名;

2.using namespace命名空间名;(使用cout:using namespace std)

       前一种形式将指定的标识符暴露在当前的作用域内,使得在当前作用域中可以直接引用该标识符;后一种形式将指定命名空间内的所有标识符暴露在当前的作用域内,使得在当前作用域中可以直接引用该命名空间内的任何标识符。

特殊的命名空间:全局命名空间和匿名命名空间

      全局命名空间实际上就是程序的全局作用域内的所有名称(除显式声明的命名空间之外,也就是没起名字,作用在全局作用域之内,就在全局命名空间内)

     匿名命名空间尝尝用来屏蔽不希望暴露给其他源文件的标识符,该作用域内的名称只在定义它们的源文件中可见,而不需要在头文件中进行声明。(本文件中调用时直接拿名字即可,不需要::,有点类似于static)

匿名对象:

       在C++中,匿名对象是指那些没有名字的对象,它们通常是通过临时表达式创建的。这些临时对象在它们的生命周期内(一般是调用的表达式)是有效的,但在表达式结束时就会被销毁。

       匿名对象通常不提供名称,因此它们无法被外部访问。这主要是因为它们是临时的,而且没有显式的名称来引用它们。匿名对象通常用于构造函数、函数参数、返回值、表达式等,它们在生命周期结束时就会被销毁。

      需要注意的是,匿名对象在使用时位const属性,也就是说,下列语句会报错:

Clock &ref = Clock(10,20,30);

做法:

左值引用,并且加入const:

const Clock &ref = Clock(10,20,30);

右值引用(上式的一种优化,因为const 左值显然没有意义):

Clock &&ref = Clock(10,20,30);

注意以下代码:

int &&ref = 10;
ref = 100;
cout << ref << endl;

      此时输出为100,但是显然10是一个常量,更确切的说是一个匿名对象,显然其时只读的,但是ref的输出变为了100,这意味着ref在栈区另外开辟了一个变量,其值为100,而此时10

对象的生存期与可见性:

可见性:

     可见性其实就是小的作用域可以屏蔽大的作用域中的同名变量,这点与C语言相似

生存期:

静态生存期:如果对象的生存期与程序的运行期相同,则称它具有静态生存期。(也就是static修饰的变量以及全局变量)

动态生存期:诞生于声明点,结束于块执行完毕之时

类的静态成员:

        类属性是描述类的所有对象共同特征的一个数据项,对于任何对象实例,它的属性值是相同的。举个例子,就像一个公司的名字,无论来几个员工,它都是一样的,且只有一份(意味着它是static),这时,它的生存期变为了静态生存期,

静态数据成员:

        此时,由于静态数据成员不属于任何一个对象,类名::标识符,--->在类的定义中只进行声明,而在使用时进行类名限定定义性声明(由于未分配对象时没有空间,所以要额外定义,通过其静态生命周期来推断出其应该使用全局变量的方式来定义,且此时不需要static修饰)

class SingelTon
{
public:
    static SingelTon *getIntance()
    {
        if(m_p == NULL)
        {
            m_p = new SingelTon;
        }
        return m_p;
    }

    static void destroyIntance()
    {
        delete m_p;
        m_p = NULL;
    }

    static int getCounter(){return counter;}

private:
    SingelTon()
    {
        ++counter;
    }
    SingelTon(const SingelTon &){}

    ~SingelTon()
    {
        --counter;
    }
    static SingelTon *m_p; //声明
    static int counter;
};

SingelTon *SingelTon::m_p = NULL;  //限定性声明  
int SingelTon::counter = 0;

静态函数成员:

   与数据成员相似,只有一份,静态生存期,可以直接通过类名调用,但是:

   1.无this指针:静态成员函数内部没有this指针,这意味着它们不能直接访问非静态成员变量或非静态成员函数。

   2.不能访问非静态成员:由于没有this指针,静态成员函数不能直接访问非静态成员变量或非静态成员函数。如果需要访问这些成员,可以通过类名或对象名来访问。

int main () {
    Point : : showCount () ;
    /直接通过类名调用函数,输出对象个数的初始值
    Point a (4,5);
    cout<< "Point A,"<<a.getx ()<<","<<a.getY ();a.showCount ( ) ;
    Point b (a) ;
    cout<< "Point B, "<<b.getx ( )<<","<<b.getY () ;B.showcount ( ) ;
}

常对象:

    常对象必须进行初始化,且在整个生存周期内不能被更新

    const 类型说明符 对象名

const int n= 10;
//正确,用10对常量n进行初始化
n= 20;
//错误,不能对常量赋值

const修饰的类成员:

1.常成员函数

使用const关键字修饰的函数为常成员函数﹐常成员函数声明的格式如下:

类型说明符 函数名(参数表) const ;

注:

1.const是函数类型的一个组成部分,所以不论声明还是定义都需要加上const

2.如果一个对象是常对象,那么该常对象只能调用其常成员函数,而不能调用其他的成员函数

3.无论是否通过常对象调用,目的对象都视为常对象,因此常成员函数不能更新目的对象的数据成员,也不能调用没有用const修饰的成员函数

4.const函数可以用于重载函数的区分:

常对象调用常成员函数

非const的对象一般调用不用const修饰的成员函数        

2.常数据成员

常数据成员只能通过初始化列表来进行初始化

而静态成员要在类外说明以及初始化

3.常引用

  在声明引用时使用const修饰,那么此时声明的引用就是常引用。-->常引用的对象不能被更新

  声明形式:const 类型说明符 & 对象名;

  const引用后,其在参数传递时可以传递常对象

  需要注意的是,const不仅可以引用常对象,也可以引用普通对象,而在引用普通对象时,将这个普通对象当做常对象

 在实际使用时,如果不需要对实参进行更改,使用常引用可以避免意外对实参修改

class 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 &p1,Point &p2);//友元函数声明
private:
     int x,y;

};

float dist (const Point &p1,const Point &p2)
{
    //友元函数实现
    double x = p1.x - p2.x;
    //通过对象访问私有数据成员
    double y = p1.y - p2.y;
    return static_cast<float> (sqrt(x*x + y*y));
}

友元函数与友元类:

友元函数:

   友元函数是在类中运用关键字friend修饰的非成员函数,但是在其函数体中可以通过对象名访问类的私有或保护成员

   也就是相当于:有一个函数void test()(不在类内),只要在类内声明friend void test();,那么此时该函数可以访问器私有或保护成员

class 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 &p1,Point &p2);//友元函数声明
private:
     int x,y;

};

float dist (Point &p1,Point &p2)
{
    //友元函数实现
    double x = p1.x - p2.x;
    //通过对象访问私有数据成员
    double y = p1.y - p2.y;
    return static_cast<float> (sqrt(x*x + y*y));
}

友元类:

         同友元函数一样若A类为B类的友元类,则A类的所有成员函数都是B类的友元函数﹐都可以访问B类的私有和保护成员。语法形式:

class B{
    //B类的成员声明
    ……
    friend class A;
    ……
    //声明A为B的友元类
};

注:

      1.友元关系是不能被传递的,B类是A类的友元类,C类是B类的友元类,但是C类并不是A类的友元类

      2.友元关系是单向的,B类是A类的友元,B类可以访问A类的私有或保护数据,但是A类不能访问B类的私有或保护数据

      3.友元关系不能被继承,也就是B的派生类不能继承B的友元。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值