【C++】4.一些特殊情况(上)


4.0概要

学习目的:

  1. 掌握类中的特殊成员
  2. 掌握友元模式
  3. 掌握单例模式

课程内容:

  1. this指针
  2. static成员
  3. const成员
  4. friend

重点难点:

  1. 静态成员
  2. 友元关系

4.1 this指针

  1. this指针是系统自动生成的,且隐藏的。
  2. this指针不是对象的一部分,作用域在类内部。
  3. 类的普通函数访问类的普通成员时,this指针总是指向调用者对象。
  4. 也就是说this指针会藏在所有的普通成员函数里面,谁调用当前这个函数,this指针就会指向谁。

在这里插入图片描述

数据成员是每个对象都有单独的一份。

而成员函数整个类只有一份,用的时候哪个对象需要去调用这个成员函数,那他就归哪个成员函数调用。


如果一个类里面有个成员叫张三,在一个函数里面也传了一个相同名字的参数,那么如何去区别这两个张三呢?

正常:

class MyClass {
public:
    int num;
    void setNum(int n) {
        num = n;
    }
    int getNum() {
        return num;
    }
};

int main() {
    MyClass obj_1;
    obj_1.setNum(10);
    cout << "num = " << obj_1.getNum() << endl;

    return 0;
}

不正常:

class MyClass {
public:
    int num;
    void setNum(int num) {     //区分不了num了
    num = num;
    }
    int getNum() {
        return num;
    }
};

int main() {
    MyClass obj_1;
    obj_1.setNum(10);
    cout << "num = " << obj_1.getNum() << endl;

    return 0;
}

this指针:

class MyClass {
public:
    int num;
    void setNum(int num) {
        //num = num;
        this->num = num;//名字相同用this指针来进行区分
    }//this指针不能改指向
    int getNum() {
        return num;//return this->num;//此处this省略了
    }
    MyClass getThis1() {//返回这个函数自己
        return *this;//返回一个对象的值
    }
    MyClass* getThis2() {//这个也可以
        return this;//返回一个指向对象的指针
    }
};

int main() {
    MyClass obj_1;
    obj_1.setNum(10);
    cout << "num = " << obj_1.getNum() << endl;

    return 0;
}

就像这个int getNum();表面上是返回return num;,实际上是返回return this->num;


4.2 static成员

  1. 在C++类中声明成员时,加上static关键字就是static成员了。
  2. 静态成员分为静态数据成员静态函数成员

静态数据成员

class ClassName {
public:
    ClassName();
    ~ClassName();
    static int num;          //静态数据成员

};

ClassName::ClassName() {
    num = 0;                  //这里直接给num=0会报错
}
ClassName::~ClassName() {}

int main() {
    ClassName obj_1;
    cout << "num = " << obj_1.num << endl;
    return 0;
}

上面会错,为什么呢?

因为static int num;

怎么解决?

class ClassName {
public:
    ClassName();
    ~ClassName();
    static int num;

};
int ClassName::num = 0;                //多了这行

ClassName::ClassName() {}              //去掉了原来的num=0
ClassName::~ClassName() {}

int main() {
    ClassName obj_1;
    cout << "num = " << obj_1.num << endl;
    return 0;
}

在类的外面给上一个int ClassName::num = 0; 就可以了。


一个类不管有多少个对象。所有的对象都全部共享着同一段静态数据成员的内存。

class ClassName {
public:
    ClassName();
    ~ClassName();
    static int num;

};
int ClassName::num = 0;

ClassName::ClassName() {}
ClassName::~ClassName() {}

int main() {
    ClassName obj_1;
    cout << "obj_1.num = " << obj_1.num << endl;

    obj_1.num = 10;

    ClassName obj_2;
    cout << "obj_2.num = " << obj_2.num << endl;

    return 0;
}
在这里插入图片描述在这里插入图片描述

这里面,通过添加一行obj_1.num = 10;就让obj_2.num的值改变了。说明用的是同一个内存。

在这里插入图片描述

sizeof(obj_1)=1;说明obj_1占用1个字节。

而上面的static int num;是占用4个字节的。

这说明num不在obj_1里面。它存在这个类里面。

在这里插入图片描述

上面说num存在类里面,那么为什么sizeof(ClassName) = 1;呢?

那是因为类型是求不了字节数的,所以 他给你创建了一个对象,既然是对象,那就是一个字节。

int main() {
    ClassName obj_1;
    cout << "obj_1.num = " << obj_1.num << endl;

    obj_1.num = 10;

    ClassName obj_2;
    cout << "obj_2.num = " << obj_2.num << endl;

    cout << "sizeof(obj_1) = " << sizeof(obj_1) << endl;
    cout << "sizeof(ClassName) = " << sizeof(ClassName) << endl;

    return 0;
}

静态成员函数

class ClassName {
public:
    static int num;   
    static void testFunc_1();
    static void testFunc_2(){
      cout << "ClassName::testFunc_2()" << endl;
    }
};
int ClassName::num = 0;

void ClassName::testFunc_1() {                  //说的是这个函数
    cout << "ClassName::testFunc_1()" << endl;
}

这个静态成员函数只在类的里面有static,类的外面不要写static。

class ClassName {
public:
    ClassName();
    ~ClassName();
    int val;
    static int num;

    static void testFunc_1();
    static void testFunc_2() {
        cout << "ClassName::testFunc_2()" << endl;
    }
};
int ClassName::num = 0;

void ClassName::testFunc_1() {
    cout << "ClassName::testFunc_1()" << endl;
}
ClassName::ClassName() {      //构造函数的计数功能实现
    num++;//计数
}
ClassName::~ClassName() {}

int main() {
    ClassName obj_1;
    cout << "obj_1.num = " << obj_1.num << endl;
    ClassName obj_2;
    cout << "obj_2.num = " << obj_2.num << endl;

    obj_2.testFunc_1();
    (&obj_1)->testFunc_2();//下面2行和这行一个作用。
    //ClassName* p = &obj_1;
    //p->testFunc_2();

    ClassName::num;
    ClassName::testFunc_1();
    ClassName::testFunc_2();

    return 0;
}

静态成员不能用类里面的普通的数据成员和普通的成员函数,甚至连this指针都没有。因为静态成员的生命周期长。

static int num;
static void testFunc_1();
static void testFunc_2() {
    cout << "ClassName::testFunc_2()" << endl;
}

单例模式

单例模式:一种设计模式,要求创建一个只能实例化一个对象的类。

class SingleInstance {
public:
    ~SingleInstance();
    static SingleInstance* getInstance();
private:
    SingleInstance();//防止类在外面创建对象
    SingleInstance(const SingleInstance& obj);//防止拷贝构造
    static SingleInstance* pInstance;//创建一个静态指针。这个静态指针也是一个静态的数据成员
};

SingleInstance* SingleInstance::getInstance() {
    if (pInstance==NULL) { 
        pInstance = new SingleInstance;
    }
    return pInstance;
}

SingleInstance* SingleInstance::pInstance = NULL;

SingleInstance::SingleInstance() {

}
SingleInstance::SingleInstance(const SingleInstance& obj) {

}
SingleInstance::~SingleInstance() {

}

int main() {
    SingleInstance* p1 = SingleInstance::getInstance();
    SingleInstance* p2 = SingleInstance::getInstance();
    SingleInstance* p3 = SingleInstance::getInstance();

    cout << hex << p1 << endl;
    cout << hex << p2 << endl;
    cout << hex << p3 << endl;

    return 0;
}

在这里插入图片描述

为什么这3个指针一样呢?

因为pInstance这个对象虽然初始化的时候是为空的,但是在if (pInstance==NULL) 创建成功了就不为空了,不为空就直接return ipInstance;了。


单例模式代码讲解
class SingleInstance {
public:
    ~SingleInstance();
    static SingleInstance* getInstance();
private:
    SingleInstance();//防止类在外面创建对象
    SingleInstance(const SingleInstance& obj);//防止拷贝构造
    static SingleInstance* pInstance;//创建一个静态指针。这个静态指针也是一个静态的数据成员
};

SingleInstance* SingleInstance::pInstance = NULL;

SingleInstance* SingleInstance::getInstance() {
    if (pInstance==NULL) { 
        pInstance = new SingleInstance;
    }
    return pInstance;
}

SingleInstance::SingleInstance() {}
SingleInstance::SingleInstance(const SingleInstance& obj) {}
SingleInstance::~SingleInstance() {}

int main() {
    SingleInstance* p1 = SingleInstance::getInstance();
    SingleInstance* p2 = SingleInstance::getInstance();
    SingleInstance* p3 = SingleInstance::getInstance();

    cout << hex << p1 << endl;
    cout << hex << p2 << endl;
    cout << hex << p3 << endl;

    return 0;
}
  1. SingleInstance 类定义:
    • ~SingleInstance();:这是一个析构函数的声明,它将在对象生命周期结束时被调用以释放资源。
    • static SingleInstance* getInstance();:这是一个静态成员函数,用于获取类的唯一实例。
    • SingleInstance();:这是一个私有构造函数的声明,它防止了类的外部实例化。
    • SingleInstance(const SingleInstance& obj);:这是一个私有拷贝构造函数的声明,它防止了类的拷贝。
    • static SingleInstance* pInstance;:这是一个静态指针成员,用于存储类的唯一实例。
  2. 静态成员初始化:
    • SingleInstance* SingleInstance::pInstance = NULL;:在类外部初始化静态成员 pInstanceNULL
  3. getInstance 函数实现:
    • 这个函数检查静态指针 pInstance 是否为 NULL。如果是,它将创建一个新的 SingleInstance 对象并将其地址赋给 pInstance。如果不是,它将直接返回 pInstance,即已经存在的唯一实例。
  4. main 函数:
    • main 函数中,通过调用 SingleInstance::getInstance() 三次来获取类的唯一实例的三个不同指针 p1p2p3
    • 使用 couthex 标志符来以十六进制格式打印这些指针的值。

需要注意的是,这个单例模式的实现是线程不安全的。如果在多线程环境中调用 getInstance,可能会导致多个实例被创建,因为两个线程可能同时检查 pInstance 是否为 NULL,然后都创建一个新的实例。为了确保线程安全,通常需要在 getInstance 函数中添加同步机制,例如使用互斥锁。

此外,这个实现没有考虑 SingleInstance 类的拷贝构造函数和赋值操作符的正确实现,这在实际应用中是必要的,以确保类的正确行为和资源管理。如果类需要支持拷贝,那么需要实现拷贝构造函数和赋值操作符,并确保它们正确地管理资源。如果不需要拷贝,可以将拷贝构造函数和赋值操作符声明为 delete

为什么pInstance不为NULL

pInstance 是一个静态成员变量,它被初始化为 NULL。静态成员变量属于类本身,而不是类的任何特定实例。这意味着无论创建多少个类的实例,静态成员变量只有一个副本。

SingleInstance 类中,pInstance 被用来存储类的唯一实例的地址。当第一次调用 getInstance() 方法时,pInstance 仍然是 NULL,因此会创建一个新的 SingleInstance 对象,并将 pInstance 指向这个新创建的对象。之后,无论调用多少次 getInstance() 方法,只要 pInstance 不为 NULL,它就会直接返回已经存在的实例的地址,而不会再次创建新的实例。

因此,pInstance 在第一次调用 getInstance() 之后就不会为空了,它会一直保持对唯一实例的引用,直到程序结束或者在某个时刻显式地删除这个实例。


后续调用 getInstance() 方法时的行为:

在第一次调用 getInstance() 方法时,我们已经讨论了以下步骤:

  1. 检查 pInstance 是否为 NULL
  2. 如果是 NULL,则创建一个新的 SingleInstance 对象。
  3. pInstance 指向这个新创建的对象。
  4. 返回 pInstance 的值。

现在,假设我们已经完成了第一次调用,并且 pInstance 已经指向了一个有效的 SingleInstance 对象。当后续调用 getInstance() 方法时,会发生以下步骤:

  1. 调用 getInstance() 方法
    当后续调用 getInstance() 方法时,程序会再次进入这个函数。

  2. 检查 pInstance 是否为 NULL
    getInstance() 函数内部,再次检查静态成员变量 pInstance 是否为 NULL。由于 pInstance 已经被第一次调用时初始化,它现在指向一个有效的 SingleInstance 对象,因此它不再是 NULL

  3. 直接返回 pInstance
    由于 pInstance 不是 NULL,程序不需要再次执行 new SingleInstance 来创建新的对象。相反,getInstance() 函数直接返回 pInstance 的值,即之前创建的 SingleInstance 对象的地址。

  4. 返回 pInstance 的值
    getInstance() 函数返回 pInstance 的值,这意味着后续调用将返回同一个对象的地址。

这个过程确保了无论调用 getInstance() 多少次,都只会创建一个 SingleInstance 对象,并且所有调用都会返回这个对象的引用。这是单例模式的关键特性,它保证了全局访问点的唯一性和对象的单一实例。

总结一下,后续调用 getInstance() 方法时,由于 pInstance 已经指向了一个有效的 SingleInstance 对象,getInstance() 函数将直接返回这个对象的地址,而不会再次创建新的对象。这样,无论何时何地,只要调用 getInstance(),都会得到同一个 SingleInstance 对象的引用。


这里是一个简化的例子来说明这个过程:

class SingleInstance {
public:
    static SingleInstance* getInstance() {
        if (pInstance == NULL) {              //第一次调用的时候,pInstance确实=NULL,满足pInstance == NULL这个判断
            pInstance = new SingleInstance;   //因此会创建一个新的 SingleInstance 对象,并将 pInstance 指向这个新创建的对象
        }
        return pInstance;//函数getInstance()返回pInstance的值,也就是新创建的SingleInstance对象的地址
    }

private:
    static SingleInstance* pInstance;
    SingleInstance() {
        // 构造函数的实现
    }
    // 其他成员函数和数据成员
};

// 静态成员变量初始化
SingleInstance* SingleInstance::pInstance = NULL;

int main() {
    SingleInstance* p1 = SingleInstance::getInstance(); // 创建唯一实例
    SingleInstance* p2 = SingleInstance::getInstance(); // 返回已存在的实例
    SingleInstance* p3 = SingleInstance::getInstance(); // 返回已存在的实例

    // p1, p2, 和 p3 都将指向同一个实例
    // 因此,它们的地址将是相同的
    cout << hex << p1 << endl; // 打印实例的地址
    cout << hex << p2 << endl; // 打印相同的地址
    cout << hex << p3 << endl; // 打印相同的地址

    return 0;
}

在这个例子中,pInstance 在第一次调用 getInstance() 时被初始化为指向新创建的 SingleInstance 对象。之后,无论调用多少次 getInstance(),它都会返回同一个对象的地址。因此,pInstance 不会为空,它会一直指向同一个实例。

  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值