Day04

Day04

一、静态成员

1.在类定义中,成员变量和成员函数都可以用static声明为静态的,称为静态成员。无论该类实例了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。

2.静态成员变量、静态成员函数的访问也是有权限的

3.静态的成员函数被所有对象共享

4.静态成员函数可以访问静态成员变量,但不能访问非静态的成员变量

5.均可通过两种方式访问。一种是实例化对象,一种是用过类名。

二、单例模式

单例是设计模式里面的一种,全局有且只有一个类的static实例,在程序任何地方都能够调用到。(某些东西只有一个,单独且唯一的一个例子)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
​
// 单例模式
class ChairMan
{
public:
    static ChairMan *getInstance()  //由于构造函数的私有化,通过此函数提供一个外界访问的接口函数
    {
        return theSingalMan;
    }
​
private:
    ChairMan(){};                 // 构造函数私有化,不可以创建多个对象
    ChairMan(const ChairMan &){}; // 拷贝构造函数私有化,防止拷贝,实现真的有且只有
​
private:                           // 只提供对外只读接口
    static ChairMan *theSingalMan; // 共享访问,类内声明,类外初始化
                                   // 定义一个唯一指向实例的静态指针,并且是私有的。
};
​
ChairMan *ChairMan ::theSingalMan = new ChairMan;
​
int main()
{
    // ChairMan *c1 = ChairMan::theSingalMan;  //有缺陷:提供了可读可写的权限
    // ChairMan *c2 = ChairMan::theSingalMan;
    // if (c1 == c2)
    // {
    //     cout << "yes" << endl;
    // }
    // else
    // {
    //     cout << "NO" << endl;
    // }
    ChairMan *c1 = ChairMan::getInstance();
    ChairMan *c2 = ChairMan::getInstance();
    if (c1 == c2)
    {
        cout << "yes" << endl;
    }
    else
    {
        cout << "NO" << endl;
    }
    system("pause");
    return EXIT_SUCCESS;
}

设计步骤:

(1)声明一个类,此类满足全局有且只有的条件

(2)私有化构造函数和拷贝构造函数

(3)定义一个指向实例的静态指针,并且设置为私有。类内定义,类外声明

(4)公有化一个外界访问该实例的静态接口函数

(5)通过类名调用接口函数实现访问实例

三、Static的修饰作用

1.因为静态数据成员不属于类的任何一个对象,所以它不是在创建类的对象的时候被定义和初始化的。

2.static修饰类内数据成员时:

(1)表明这个数据成员独立于所有对象之外,所以只能在外部定义和初始化每个静态(数据)成员。并且一个静态数据成员只能定义一次。 (2)static成员变量的内存在所有对象之外,保存在全局数据区,所以它的声明周期等同于整个程序的周期,static数据成员被定义后就一直存在于程序的整个生命周期中。

3.static修饰成员函数时:

(1)带有static关键字的类成员函数能访问的变量只有、全局变量、参数、类内static数据成员

(2)共享访问,类内声明,类外初始化

(3)静态函数成员不持有this指针,不能声明为const(常量成员函数)

四、函数的构造和析构

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
​
class Person
{
public:
    Person()
    {
        cout << "person的构造函数调用" << endl; // 构造时自动调用
    };
    ~Person() //此处需要增加一个~符号,函数名和构造函数名是一致的
    {
        cout << "析构函数调用" << endl; // 生命周期结束后自动调用
    }
};
​
int main()
{
    Person p;
    system("pause");
    return EXIT_SUCCESS;
    
}

五、构造函数分类

1.有参和无参构造

class Person
{
public:
    Person()
    {
        cout << "默认构造函数" << endl;
    }
    Person(int a)
    {
        cout << "有参构造" << endl;
    }
};

2.普通构造函数和拷贝构造函数

class Person
{
public:
    Person()
    {
        cout << "默认构造函数" << endl;
    }
    Person(int a)
    {
        age = a;
        cout << "有参构造" << endl;
    }
    Person (const Person &p)
    {
         age = p.age;
    }
    int age;
};

3.调用方法

Person p(10); //括号法
    Person p = Person(100); //显示法
    Person p3 = 10; //=Person p3 = Person(10); 隐式法

注意:

(1)括号法不能调用无参构造

(2)不要用拷贝狗杂初始化匿名对象

六、拷贝函数调用时机

1.用已将创建好的对象来初始化新的对象

void test()
{
    Person p1(18);
    Person p2 = Person(p1);
    cout << "p2的年龄=" << p2.age << endl;
}

2.值传递的方式给函数参数传值

void doWork(Person p)
{
}
void test2()
{
    Person p1(100);
    doWork(p1);
}

3.以值方式,返回局部对象(优化后,不会调用拷贝构造)

Person doWork2()
{
    Person p;
    return p;
}
void test3()
{
    Person p = doWork2();
}

七、构造函数的调用规则

1.编译器会给一个类至少添加三个函数:默认构造、析构函数、拷贝构造

2.如果我们自己提供了有参构造函数,编译器就不提供默认构造函数,但依然会提供拷贝函数

3.若我们提供了拷贝构造函数,编译器就不会提供其他构造函数

八、深浅拷贝

1.编译器提供的默认拷贝构造函数,做了简单的值拷贝(浅拷贝)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string.h>
using namespace std;

class Person
{
public:
    Person(char *name, int age)
    {
        myName = (char *)malloc(strlen(name) + 1);
        strcpy(myName, name);
        myAge = age;
    }
    ~Person() //调用析构函数时会重复释放堆区内存,引发错误
    {
        if (myName != NULL)
        {
            free(myName);
            myName = NULL;
        }
    }
    char *myName;
    int myAge;
};
void test()
{
    Person p("Louis", 18);
    cout << "姓名=" << p.myName << ",age=" << p.myAge << endl;
    Person p2(p);
    cout << "姓名=" << p2.myName << ",age=" << p2.myAge << endl;
}
int main()
{
    test();
    system("pause");
    return EXIT_SUCCESS;
}

2.深拷贝解决该问题(自己重写拷贝构造函数)

Person(const Person &p)
{
    myName = (char *)malloc(strlen(p.myName) + 1);
    strcpy(myName, p.myName);
    myAge = p.myAge;
};

九、初始化列表语法

class Person
{
public:
    // Person(int a, int b, int c)
    // {
    //     ma = a;
    //     mb = b;
    //     mc = c;
    // }
    Person() : ma(10), mb(20), mc(30) //冒号是关键
    {
    }
    int ma;
    int mb;
    int mc;
};

void test()
{
    Person p;
    cout << "Ma=" << p.ma << endl;
    cout << "Mb=" << p.mb << endl;
    cout << "Mc=" << p.mc << endl;
}

十、explicit关键字使用

防止利用隐式法来实例化对象,避免引发歧义

十一、new和delete

class Person
{
public:
    Person()
    {
        cout << "Person构造函数" << endl;
    }
    ~Person()
    {
        cout << "Person析构函数" << endl;
    }
};

void test()
{
    Person *p = new Person;
    delete p;
} 

1.malloc和new的区别

(1)malloc和free属于是库函数,new和delete属于是运算符

(2)malloc不会调用构造函数,new会调用构造函数

(3)malloc返回void*需要强转,new返回的是创建的对象的指针

2.注意事项

(1)不要用void*去接收new出来的对象

(2)堆区开辟数组时候,一定会调用无参构造函数。释放时加一个中括号。

(3)栈上开辟数组,可以没有默认构造

void test2() // 堆区开辟数组等
{
    // int * pInt = new int[10];
    // 堆区开辟数组时候,一定会调用无参构造函数
    Person *pPerson = new Person[10];
    delete[] pPerson;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值