03-C++ 类和对象

类和对象

1. 概述

1.1 对象

真实存在的事物

1.2 类

多个对象抽取其共同特点形成的概念

静态特征提取出来的概念称为成员变量,又名属性
动态特征提取出来的概念称为成员函数,又名方法

1.3 类与对象的关系

在代码中先有类后有对象
一个类可以有多个对象
多个对象可以属于同一个类

2. 类的定义

2.1 语法

class 类名
{
    [访问权限修饰符:]
    成员变量
    成员函数
};

2.2 访问权限修饰符

private: 私有的,当前类中可用,默认的

protected: 受保护的,当前类或子类中可用

public: 公共的,当前项目中可用

2.3 示例

class Person{
private:
	int age;
protected:
	char sex[10];
public:
	char name[50];
    void eat()
    {
    	cout << name << "吃饭" << endl;
    } 
    void sleep();
};

void Person::sleep()
{ 
    cout << name << "睡觉" << endl;
}

3. 封装性

概念:即包装,将数据和方法封装在一起,加以权限区分使其可以保护内部,降低耦合度,便于使用

int a = 10;
int nums[5] = {1,2,3,4,5};
void fun()
{
	xxx
} 
class A
{
    属性
    函数
} 
A a;
x.c文件

优点:

  1. 降低代码耦合度
  2. 提高代码复用率
  3. 便于使用

4. 类的设计

① 私有化所有属性

② 提供可以获取这些属性值与修改属性值的函数

示例:

#include 
#include 

using namespace std;

class Dog{
private:
    char name[30];
    int age;
public:
    void set_name(char *n)
    {
        strcpy(name, n);
    }
    void set_age(int a)
    {
        age = a;
    }

    char *get_name()
    {
        return name;
    }
    int get_age()
    {
        return age;
    }

    void look_dor()
    {
        cout << name << "看门" << endl;
    }
    void eat()
    {
        cout << name << "吃" << endl;
    }
};

int main(int argc, char *argv[])
{
    Dog d1;
    d1.set_name("大黄");
    d1.set_age(2);
    char *name = d1.get_name();
    int age = d1.get_age();
    cout << "姓名:" << name << " 年龄:" << age<< endl;
    d1.look_dor();

    Dog d2;
    d2.set_name("旺财");
    d2.set_age(1);
    char *name2 = d2.get_name();
    int age2 = d2.get_age();
    cout << "姓名:" << name2 << " 年龄:" << age2 << endl;
    d2.eat();
    return 0;
}
//姓名:大黄 年龄:2
//大黄看门
//姓名:旺财 年龄:1
//旺财吃

4.1 练习

请设计一个 Person 类,Person 类具有 name 和 age 属性,提供初始化函数(Init)
并提供对 name 和 age 的读写函数(set,get)
但必须确保 age 的赋值在有效范围内(0-100),超出有效范围,则拒绝赋值
并提供方法输出姓名和年龄
#include 
#include 
using namespace std;
class Person
{
private:
    char name[50];
    int age;
public:
    void init(char *n,int a)
    {
        strcpy(name,n);
        if(a < 0 || a > 100)
        {
            age = 0;
            return;
        }
        age = a;
    }
    void set_name(char *n)
    {
        strcpy(name,n);
    }
    char* get_name()
    {
        return name;
    }
    void set_age(int a)
    {
        if(a < 0 || a > 100)
        {
            age = 0;
            return;
        }
        age = a;
    }
    void print_info()
    {
        cout << name << endl << age << endl;
    }
};
int main(int argc, char *argv[])
{
    Person p;
//    strcpy(p.name,"德玛");
//    p.age = -18;
    p.set_name("德玛");
    p.set_age(-18);
    p.print_info();

    Person p2;
    p2.init("光头强",18);
    p2.print_info();
    return 0;
}
//德玛
//0
//光头强
//18

4.2 练习2:分文件

设计立方体类(Cube)
其属性有长,宽,高
其函数有获取长宽高的函数,修改长宽高的函数
计算体积的函数
计算面积的函数

提示:
    立方体的面积:2ab + 2ac + 2bc
    体积:a * b * c

main.cpp 文件:

#include 
#include "cude.h"
using namespace std;

int main(int argc, char *argv[])
{
    Cube c1;
    c1.set_h(10);
    c1.set_l(1);
    c1.set_w(2);

    c1.mj();
    c1.tj();
    return 0;
}

cube.cpp 文件

#include "cude.h"
#include 
using namespace std;
int Cube::get_l()
{
    return l;
}
int Cube::get_w()
{
    return w;
}
int Cube::get_h()
{
    return h;
}

void Cube::set_l(int length)
{
    l = length;
}

void Cube::set_w(int width)
{
    w = width;
}
void Cube::set_h(int height)
{
    h = height;
}

void Cube::mj()
{
    int x = 2 * l * w + 2 * l * h + 2 * w * h;
    cout << "立方体面积为:" << x << endl;
}
void Cube::tj()
{
    int x = l * w * h;
    cout << "立方体体积为:" << x << endl;
}

cube.h 文件

#ifndef CUDE_H
#define CUDE_H
class Cube
{
private:
    int l;
    int w;
    int h;
public:
    int get_l();
    int get_w();
    int get_h();
    void set_l(int length);
    void set_w(int width);
    void set_h(int height);
    void mj();
    void tj();
};
#endif // CUDE_H

5. 构造函数

5.1 概述

构造函数:类实例化对象的时候自动调用。

注意:

  • 当一个类中 没有构造函数系统将默认 为其 生成一个无参构造
  • 如果一个类中有构造函数,系统将不会为其提供默认的无参构造
  • 一个类 可以 定义多个 构造函数,该类中的 多个构造函数为重载关系

无参构造:

  • 构造函数无形参列表

有参构造:

  • 构造函数有形参列表

5.2 语法

类名(形参列表)
{ 
	该类对象赋初始值
} 

注意:形参列表可有可无

5.3 示例

#include 

using namespace std;
class A{
private:
    int a;
public:
    A()
    {
        cout << "无参构造被调用了" << endl;
    }
    A(int x)
    {
        cout << "有参构造被调用了" << endl;
    }
    void test()
    {
        cout << "test" << endl;
    }
};

int main(int argc, char *argv[])
{
    //隐式调用无参构造
    //类名 对象名
    A a1;
    //显式调用无参构造
    //类名 对象名 = 构造函数名();
    A a2 = A();

    //隐式调用有参构造
    A a3(10);
    //显式调用有参构造
    A a4 = A(20);

    //隐式转换(了解)
    //当调用的构造函数中只有一个参数时可用
    A a5 = 10;
    cout << "********************" << endl;
    //匿名对象
    //没有对象名的对象
    // 注意:一个匿名对象只能使用一次
    A().test();
    A(1000).test();
    return 0;
}
//无参构造被调用了
//无参构造被调用了
//有参构造被调用了
//有参构造被调用了
//有参构造被调用了
//********************
//无参构造被调用了
//test
//有参构造被调用了
//test

6. 析构函数

6.1 概述

对象生命周期结束的时候,自动调用析构函数。

注意:

  • 一个类 只能有 一个析构函数
  • 如果 用户不提供 析构函数 编译器 默认会提供一个空的析构函数

经验:

  • 一般不需要 自定义析构函数,但是如果类中有指针成员且指向堆区空间,这时 必须实现析构函数,在其中释放指针成员指向的堆区空间

6.2 语法

~类名()
{
	
}

注意:没有形参列表

6.3 示例

#include 
#include 
#include 
using namespace std;
class Data{
private:
    int a;
    int b;
    char *msg;
public:
    Data()
    {
        msg = NULL;
    }
    Data(int x, int y)
    {
        a = x;
        b = y;
        msg = NULL;
    }
    Data(int x, int y, char *m)
    {
        a = x;
        b = y;
        msg = (char *)malloc(50);
        strcpy(msg, m);
    }
    ~Data()
    {
        cout << "析构函数被调用了" << endl;
        //判断msg堆区空间是否被释放
        if(msg != NULL)
        {
            free(msg);
            msg = NULL;
        }
    }
};

void fun01()
{
    Data d1;
}
int main(int argc, char *argv[])
{
    //fun01();    //析构函数被调用了  对象生命周期结束自动触发析构函数

    Data d2;    //析构函数被调用了 打开下面的死循环,不会被调用
//    while(1);
    return 0;
}

7. 多对象构造与析构顺序

7.1 对象A与对象B平级,符合栈的顺序(先进后出),谁先创建谁后释放

#include 

using namespace std;
class A{
private:
    int a;
public:
    A()
    {
        cout << "A无参构造函数被调用了" << endl;
    }
    A(int x)
    {
        a = x;
        cout << a << "构造函数被调用了" << endl;
    }
    ~A()
    {
        cout << a << "析构函数被调用了" << endl;
    }
};

void fun01()
{
    A a1(1);
    A a2(2);
    {
        A a3(3);
        A a4(4);
    }
}

int main(int argc, char *argv[])
{
    fun01();
    return 0;
}

结果:

1构造函数被调用了
2构造函数被调用了
3构造函数被调用了
4构造函数被调用了
4析构函数被调用了
3析构函数被调用了
2析构函数被调用了
1析构函数被调用了

7.2 对象A是对象B的成员,先成员构造,再对象构造,再对象析构,再成员析构

#include 

using namespace std;
class A{
private:
    int a;
public:
    A()
    {
        cout << "A无参构造函数被调用了" << endl;
    }
    A(int x)
    {
        a = x;
        cout << a << "构造函数被调用了" << endl;
    }
    ~A()
    {
        cout << a << "析构函数被调用了" << endl;
    }
};

void fun01()
{
    A a1(1);
    A a2(2);
    {
        A a3(3);
        A a4(4);
    }
}

class B{
private:
    int b;
    A a;
public:
    B(int x)
    {
        b = x;
        cout << b << "B的构造函数被调用了" << endl;
    }
    ~B()
    {
        cout << b << "B的析构函数被调用了" << endl;
    }
};
void fun02()
{
    B b(5);
}

int main(int argc, char *argv[])
{
    //fun01();
    fun02();
    return 0;
}
//A无参构造函数被调用了
//5B的构造函数被调用了
//5B的析构函数被调用了
//4200699构造函数被调用了

8. 拷贝构造函数

8.1 概述

拷贝构造在以下情况自动触发:

  1. 旧对象给新对象初始化,会调用拷贝构造函数
  2. 对象作为函数的形参,函数调用时会调用拷贝构造
  3. 普通对象作为函数的返回值(vsCode会触发拷贝构造,Qt、Linux不会触发拷贝构造)

注意:

  • 如果 用户不提供拷贝构造 编译器会提供一个默认 的拷贝构造(浅拷贝)

  • 只有 类中有指针成员且指向堆区时 才有必要实现拷贝构造(深拷贝)。

浅拷贝与深拷贝:

  • 浅拷贝:当类中的成员有指针成员,此时只拷贝地址
  • 深拷贝:当类中的成员有指针成员,先开辟内存,再拷贝其值

8.2 语法

类名(const 类名 &ob)
{ 

}

8.3 示例

#include 
#include 
#include 
using namespace std;
class Stu{
public:
    int x;
public:
    Stu()
    {
        cout << "Stu无参" << endl;
    }
    Stu(int a)
    {
        x = a;
        cout << "Stu有参" << endl;
    }
    /*
        拷贝构造
            语法:
                类名(const 类名& 对象名){
                    函数体
                }
            拷贝构造调用情况:
                1,使用老对象初始化新对象
                2,调用的函数时,该函数的形参为对象
                3,函数的返回值是对象
    */
    Stu(const Stu& s)
    {
           cout << "拷贝构造被调用了" << endl;
           x = s.x;
    }
};

void fun01()
{
    Stu s1(10);
    //将老对象初始化新对象,会触发拷贝构造
    Stu s2 = s1;
    cout << "s1.x = " << s1.x << endl;
    cout << "s2.x = " << s2.x << endl;
    s2.x = 100;
    cout << "s1.x = " << s1.x << endl;
    cout << "s2.x = " << s2.x << endl;
}

void test01(Stu s)
{

}
void fun02()
{
    Stu s;
    test01(s);
}

Stu test02()
{
    static Stu s;
    return s;
}
void fun03()
{
    Stu s = test02();
}

int main(int argc, char *argv[])
{
    fun01();	//情况1
    fun02();	//情况2
    cout << "--------------" << endl;
    fun03();	//情况3
    return 0;
}

//Stu有参
//拷贝构造被调用了
//s1.x = 10
//s2.x = 10
//s1.x = 10
//s2.x = 100

//Stu无参
//拷贝构造被调用了
//--------------
//Stu无参
//拷贝构造被调用了

8.4 示例:浅拷贝

注意:

  • 在释放内存时,先释放p2 ,其指向的地址会被释放,但是p1的地址还是0x01,再次释放就会报错,所以浅拷贝会存在问题
#include 
#include 
#include 
using namespace std;
class Person{
private:
    int age;
    char *name;
public:
    Person(int a, char *n)
    {
        age = a;
        name = (char *)malloc(50);
        strcpy(name, n);
    }
    //浅拷贝
    //系统提供的拷贝构造就是浅拷贝
    //只拷贝地址,不拷贝值
    Person(const Person& p)
    {
        name = p.name;
        age = p.age;
    }
    
    void set_name(char *na)
    {
        strcpy(name, na);
    }

    void print_info()
    {
        cout << "姓名:" << name << "\t年龄:" << age << endl;
    }
    ~Person()
    {
        if(name != NULL)
        {
            free(name);
            name = NULL;
        }
    }
};

int main(int argc, char *argv[])
{
    Person p1 = Person(18, "张三");
    Person p2 = p1;

    p1.print_info();    //姓名:张三	年龄:18
    p2.print_info();    //姓名:张三	年龄:18

    p2.set_name("李四");
    p1.print_info();    //姓名:李四	年龄:18
    p2.print_info();    //姓名:李四	年龄:18

    return 0;
}

03-C++ 类和对象_第1张图片

8.5 示例:深拷贝

深拷贝会在堆区再开辟一块内存,将值拷贝到这块内存,即p1和 p2指向了不同的内存空间,释放时是各自释放各自的,不会存在问题

#include 
#include 
#include 
using namespace std;
class Person{
private:
    int age;
    char *name;
public:
    Person(int a, char *n)
    {
        age = a;
        name = (char *)malloc(50);
        strcpy(name, n);
    }
    //深拷贝
    Person(const Person& p)
    {
        age = p.age;
        name = (char *)calloc(1,50);
        strcpy(name, p.name);
    }

    void set_name(char *na)
    {
        strcpy(name, na);
    }

    void print_info()
    {
        cout << "姓名:" << name << "\t年龄:" << age << endl;
    }
    ~Person()
    {
        if(name != NULL)
        {
            free(name);
            name = NULL;
        }
    }
};

int main(int argc, char *argv[])
{
    Person p1 = Person(18, "张三");
    Person p2 = p1;

    p1.print_info();    //姓名:张三	年龄:18
    p2.print_info();    //姓名:张三	年龄:18

    p2.set_name("李四");
    p1.print_info();    //姓名:张三	年龄:18
    p2.print_info();    //姓名:李四	年龄:18

    return 0;
}

9. 初始化列表

9.1 概述

构造函数:主要用于创建类的对象是给其属性赋初始值

在定义构造函数时,c++中提供了初始化列表的语法,以便于初始化成员变量的值。

9.2 语法

类名(参数列表):成员名(参数名),成员名2(参数名2),... {
}

9.3 示例

#include 

using namespace std;
class A{
private:
    int a,b;
public:
    A(){}
//    A(int x,int y)
//    {
//        a = x;
//        b = y;
//    }
    A(int x,int y):a(x),b(y){}
    A(const A& obj)
    {
        a = obj.a;
        b = obj.b;
    }
    ~A()
    {

    }
    void print_info()
    {
        cout << a << "\t" << b << endl;
    }
};

class B{
public:
    int c;
    A a;
public:
    B(int z,int x,int y):c(z),a(A(x,y))
    {

    }
};
int main(int argc, char *argv[])
{
    A a(1,7);   //1	7
    a.print_info();
    B b(1,2,3);
    cout << b.c << endl; //1
    b.a.print_info();    //2 3
    return 0;
}

10. explicit关键字

作用:禁止隐式转换

语法:

explicit 类名(形参列表):初始化列表
{

}

示例:

class C{
private:
	int a;
public:
	explicit C(int x):a(x){}
};
int main(int argc, char *argv[])
{ 
    C c = 10;//构造函数的隐式转换
    //当调用构造函数使用explicit修饰后防止隐式转换,此时上述代码报错
    return 0;
}

11. new / delete

  • 当用 new 创建一个对象时,它就 在堆里为对象分配内存并调用构造函数完成初始化;

  • new 表达式的反面是 delete 表达式。 delete 表达式先调用析构函数,然后释放内存。正如 new 表达式返回一个指向对象的指针一样, delete 需要一个对象的地址。delete 只适用于由 new 创建的对象。

11.1 情况1:操作基本类型

作用:

  • new:申请堆区空间;
  • delete:释放堆区空间。

示例:

#include 

using namespace std;

void fun01()
{
    int *p1 = new int;
    *p1 = 100;
    cout << *p1 << endl;    //100

    int *p2 = new int(200); //给p2的值初始化为200
    cout << *p2 << endl;    //200

    delete p1;
    delete p2;
}

int main(int argc, char *argv[])
{
    fun01();
    return 0;
}

11.2 情况2:操作数组

作用:

  • new:申请堆区空间;
  • delete:释放堆区空间。

示例:

#include 
using namespace std;
void fun02()
{
    //int nums01[5] = {0};
    int *nums01 = new int[5];

    //int nums02[5] = {1,2,3,4,5};
    int *nums02 = new int[5]{1,2,3,4,5};
    for(int i = 0; i < 5; i++)
    {
        cout << nums01[i] << endl;
    }
    cout << "------------------" << endl;

    for(int i = 0; i < 5; i++)
    {
        cout << nums02[i] << endl;
    }
    //delete nums01;//只会释放数组中第一个元素的内存
    delete [] nums01;
    delete [] nums02;
}

int main(int argc, char *argv[])
{
    fun02();
    return 0;
}
//7798976
//7798976
//0
//0
//-150994442
//------------------
//1
//2
//3
//4
//5

11.3 情况3:操作对象

作用:

  • new:分配空间,调用构造函数 ;
  • delete:调用析构函数,释放空间 。

示例:

#include 

using namespace std;

class A{
public:
    A()
    {
        cout << "A无参" << endl;
    }
    ~A()
    {
        cout << "A析构" << endl;
    }
};
int main(int argc, char *argv[])
{
    //不会调用构造函数
//    A *a = (A *)calloc(1,sizeof(A));
    //不会调用析构函数
//    free(a);

    //会调用构造函数
    A *a1 = new A();
    //会调用析构函数
    delete a1;
//    while(1);

    return 0;
}

12. 对象数组

数组中存放的是对象。

对象数组:必须 显示 调用构造函数初始化

12.1 静态对象数组

示例:

#include 

using namespace std;
class A{
public:
    int mA;
public:
    A()
    {
        cout<<"A无参构造"<<endl;
    }
    A(int a)
    {
        mA = a;
        cout<<"A有参构造mA="<<mA<<endl;
    }
    ~A()
    {
        cout<<"A析构函数mA="<<mA<<endl;
    }
};
void fun04()
{
    //对象数组 必须显示调用构造函数初始化
    A arr[5]={A(10),A(20),A(30),A(40),A(50)};
    int n = sizeof(arr)/sizeof(arr[0]);
    int i=0;
    for(i=0;i<n;i++)
    {
        cout<<arr[i].mA<<" ";
    }
    cout<<endl;
}

int main(int argc, char *argv[])
{
    fun04();
    return 0;
}
//A有参构造mA=10
//A有参构造mA=20
//A有参构造mA=30
//A有参构造mA=40
//A有参构造mA=50
//10 20 30 40 50 
//A析构函数mA=50
//A析构函数mA=40
//A析构函数mA=30
//A析构函数mA=20
//A析构函数mA=10

12.2 动态对象数组

示例:

void fun05()
{ 
    A *arr = new A[5]{A(10),A(20),A(30),A(40),A(50)};
    int i=0;
    for(i=0;i<5;i++)
    {
    	cout<<arr[i].mA<<" ";
    } 
    cout<<endl;
    
    //delete arr;//只会释放第0个元素
    delete [] arr;
}

//A有参构造mA=10
//A有参构造mA=20
//A有参构造mA=30
//A有参构造mA=40
//A有参构造mA=50
//10 20 30 40 50 
//A析构函数mA=50
//A析构函数mA=40
//A析构函数mA=30
//A析构函数mA=20
//A析构函数mA=10

13. 静态成员

static 修饰的成员为静态成员。

建议使用 类名 调用静态成员

  • 静态成员在类加载之初被加载,不占用对象的空间
  • 非静态成员加载在对象中
  • 成员函数加载到代码区

13.1 静态成员变量

13.1.1 概述

特点:

  • 静态成员是 属于类 而不是对象。(所有对象共享)

注意:

  • 静态成员数据 不占对象的内存空间
  • 静态成员数据是属于类 而不是对象(多个对象共享一份静态成员数据)
  • 静态成员数据 在 定义对象之前 就存在。静态成员数据在类中定义,类外初始化。
13.1.2 示例
#include 
using namespace std;
class A{
public:
    static int num;
    int num03;
public:
    A(){
    }
};
//类外初始化
int A::num = 10;
int main(int argc, char *argv[])
{
    A a1 = A();
    a1.num03 = 20;
    cout << sizeof(a1) << endl;  //4
    cout << a1.num << endl;      //10
    //对象a1.num03赋值
    cout << "a1.num03 = " << a1.num03 << endl;//a1.num03 = 20

    A a2;
    //对象a2.num03没有赋值,所以是随机值
    cout << "a2.num03 = " << a2.num03 << endl;//a2.num03 = 6422384

    cout << "a1.num = " << a1.num << endl;//a1.num = 10
    cout << "a2.num = " << a2.num << endl;//a2.num = 10
    //通过a2修改num的值,a1.num也随之改变
    a2.num = 100;
    cout << "a1.num = " << a1.num << endl;//a1.num = 100
    cout << "a2.num = " << a2.num << endl;//a2.num = 100
    return 0;
}
13.1.3 小结
1,属于类的,不属于对象,当前类中所有对象共有一份,不占用对象的内存空间
2,可以使用类名直接调用,也可以使用对象名调用
	类名调用:
		类名::静态成员变量名
	对象调用:
		对象名.静态成员变量名
3,在类中定义,在类外初始化,初始化时不关注访问权限修饰符,在类外使用时关注访问权限修饰符

13.2 静态成员函数

使用 static 修饰的成员函数

特点:

  • 静态成员函数中 只能使用静态成员

示例

#include 
#include "cstring"
using namespace std;
class Stu{
private:
    static char c[50];
    char name[50];
    int age;
public:
    //有参构造函数,方便对象初始化
    Stu(char n[50],int a)
    {
        strcpy(name,n);
        age = a;
    }

    void print_info()
    {
        cout << name << endl;
        cout << age << endl;
        cout << c << endl;
    }
    //注意静态成员函数中只能使用静态成员
    static void test()
    {
        //cout << name << endl; //报错
        //cout << age << endl;
        cout << c << endl;
    }
};
//当静态为私有的
//此时可以对其进行初始化,但是不能直接使用
char Stu::c[50] = "iot2302";

int main(int argc, char *argv[])
{
    //隐式调用有参构造
    Stu s01("张三",23);
    Stu s02("李四",24);
    Stu s03("王五",23);
    s01.print_info();
    s02.print_info();
    s03.print_info();
    //使用对象名调用静态成员(public权限下)
    //strcpy(s01.c,"iot2303");
    //建议使用类名调用静态成员(public权限下)
    //cout << Stu::c << endl;

    s01.print_info();
    s02.print_info();
    s03.print_info();

    //静态成员函数,可以使用类名调用,也可以使用对象名调用
    s01.test();
    Stu::test();
    return 0;
}

//张三
//23
//iot2302
//李四
//24
//iot2302
//王五
//23
//iot2302

//张三
//23
//iot2302
//李四
//24
//iot2302
//王五
//23
//iot2302

//iot2302
//iot2302

14. 单例模式

设计模式:

每一个设计模式是为了解决 一种特点的问题

概述:所属的类 只能实例化一个对象。

思想:

  • 私有化构造函数
  • 提供公共函数,返回该类对象

14.1 饿汉式:返回创建好的对象

缺点:占用内存多

优点:线程安全

示例:

#include 

using namespace std;
class A{
private:
    int x;
    //实例化A的对象,此时是个指针
    static A *a;
private:
    A(){}
    A(const A& a){

    }
public:
    //普通函数在外部只能通过对象调用,这个类在外部没法创建对象,
    //所以只能创建为公共静态函数,在外部通过类调用。
    //这个函数是为了返回该类的对象,如果只返回A,还是会触发拷贝构造(旧对象给新对象初始化),
    //因为拷贝构造函数已经私有化,所以只能返回类的引用
    static A& getInstance()
    {
        //返回该类的对象(对a取值,就是对象)
        return *a;
    }
};
// 静态成员变量 类外初始化,开辟内存,所以是地址,用指针接收 
A *A::a = new A();
void fun01()
{
    //返回的是A的引用个,所以接收的也是A的引用这种类型
    A& a1 = A::getInstance();
    A& a2 = A::getInstance();
    A& a3 = A::getInstance();
    //打印这3个对象的地址,相同说明实例化的是同一个对象
    cout << &a1 << endl; //0x1e7030
    cout << &a2 << endl; //0x1e7030
    cout << &a3 << endl; //0x1e7030
}

int main(int argc, char *argv[])
{
    fun01();
    return 0;
}

14.2 懒汉式:第一次调用时创建对象并返回,后面调用返回第一次创建的对象

缺点:线程不安全

优点:占内存少

示例:

class B{
private:
    int x;
    static B* b;
private:
    B(){}
    B(const B& b){}
public:
    static B& getInstance()
    {
        //判断是否已经创建对象
        if(b == NULL)
        {
            b = new B();
        }
        return *b;
    }
};
B *B::b = NULL;
int main(int argc, char *argv[])
{
    B& b1 = B::getInstance();
    B& b2 = B::getInstance();
    B& b3 = B::getInstance();

    cout << &b1 << endl; //0xf37040
    cout << &b2 << endl; //0xf37040
    cout << &b3 << endl; //0xf37040
    return 0;
}

你可能感兴趣的:(C/C++,c++)