C++核心编程5——类和对象之对象特性

对象的初始化和清理

C++的面向对象来源于生活,因此每个对象也会有初始设置以及对象销毁前的清理数据的设置

1. 构造函数和析构函数

 

class Student 
{
public:
    //构建构造函数
    Student()
    {
    }
    //构建析构函数
    ~Student()
    {
    }
};

void test1()
{
    Student stu1;
}

int main()
{
   //调用test1,会在test1执行时自动调用类的构造函数和析构函数
    test1;

    /*
    //若是直接在main()函数里面调用类,会先执行构造函数,等整个main()函数执行完毕再调用析构函数
    Student stu1;
    */

    return 0;
}

2. 构造函数的分类及调用

class Student 
{
public:
    //普通构造函数
    //构建无参构造函数
    Student()
    {
        cout << "调用无参构造函数" << endl;
    }
    //构建有参构造函数
    Student(string Iname)
    {
        cout << "调用有参构造函数" << endl;
        name = Iname;
    }

    //拷贝构造函数
    Student( const Student &stu2 )
    {
        cout << "调用拷贝构造函数" << endl;
        name = stu2.name;
    }
    //构建析构函数
    ~Student()
    {
        cout << "调用析构函数" << endl;
    }

    string name;

};

//构造函数调用
void test1()
{
    //括号调用
    Student stu1;//(调用普通无参构造函数/默认构造函数)
    Student stu2("cara");//(调用普通有参构造函数)
    Student stu3(stu2);//(调用拷贝构造函数)
    cout << stu3.name << endl;

    //显示调用
    Student stu1;//(调用普通无参构造函数/默认构造函数)
    Student stu2 = Student("cara");//(调用普通有参构造函数)
    Student stu3 = Student(stu2);//(调用拷贝构造函数)

    //匿名对象
    Student("cara"); //匿名对象执行完毕后,系统会立即回收该匿名对象
    //不要使用拷贝构造函数初始化匿名对象
    Student(stu3);  //这一句定价于:Student(stu3) == Student stu3,相当于是在创建对象,而不是在初始化对象

    //隐式转换
    Student stu2 = "cara";//(调用普通有参构造函数)
    Student stu3 = stu2;  //(调用拷贝构造函数)

}

3. 拷贝构造函数调用时机

 

4. 构造函数调用规则

5. 深拷贝与浅拷贝

编译器默认的拷贝函数是浅拷贝函数

class Student 
{
public:
    //构建无参构造函数
    Student()
    {
        cout << "调用无参构造函数" << endl;
    }
    //构建有参构造函数
    Student(int age,int Height)
    {
        cout << "调用有参构造函数" << endl;
        mAge = age;
        mHeight = new int(Height); //在堆区创建数据
    }

    //拷贝构造函数
    Student( const Student &stu2 )
    {
        cout << "调用拷贝构造函数" << endl;
        mAge = stu2.mAge;
        //mHeight = stu2.mHeight;         //编译器自动生成的是浅拷贝构造函数 
        mHeight = new int(*stu2.mHeight); //程序员自己构建的深拷贝构造函数,在堆区重新开辟一块内存空间
    }

    int mAge;
    int* mHeight;  //访问堆区的数据返回的是一个指针

    //构建析构函数
    ~Student()
    {
        if (mHeight != 0)
        {
            delete mHeight;
            mHeight = NULL;
        }
        cout << "调用析构函数" << endl;
    }

};

//构造函数调用
void test1()
{
    Student stu1;
    Student stu2(18,180);
    Student stu3(stu2);
}

int main()
{
    test1();
    return 0;
}

浅拷贝的一个问题:堆区内存的重复释放。

我们在堆区开辟一块内存用于存储stu2的身高,在栈区则存放着身高的内存地址。Student stu3(stu2)会将stu2的所有属性浅拷贝给stu3,包括年龄和身高的地址。根据栈区的释放规则(先进后出),stu3会首先执行析构函数,去释放堆区的内存空间。随后stu2再执行其析构函数,他会再去释放已经被释放过的堆区内存空间,因此会报错。

浅拷贝示意图:

深拷贝会在堆区重新开辟一块内存空间用于存储stu3的身高,因此不会出现重复释放的问题

深拷贝示意图:

6. 初始化列表

C++提供了初始化列表语法,用来初始化属性

    //普通初始化操作方式
    Student(int age,int Height)
    {
        mAge = age;
        mHeight = Height; 
    }

    //初始化列表操作方式
    Student(int age, int Height): mAge(age), mHeight(Height)   //或者:Student: mAge(10), mHeight(20) 
    {
       
    }

7. 类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为对象成员

先调用A,再调用B

8. 静态成员

class Student 
{
public:
    static int a;  //静态成员变量必须在类内进行声明

    static void func()
    {
        a = 100;  //静态成员函数只能访问静态成员变量,不能访问非静态成员变量
                  //因为静态成员函数是所有对象共享,而非静态成员变量是特定对象的属性,因此在静态成员函数内部访问非静态成员变量时,函数无法区分该
    }
};

int Student::a = 0;  //静态成员变量必须在类外进行初始化

C++对象模型和this指针

1. 成员变量和成员函数分开储存

只有非静态成员变量属于类的对象上,其他的(非静态成员函数、静态成员变量、静态成员函数) 都不属于。

空类所占内存为1,C++会为每一个空内存分配一个内存空间,以便于为彼此做区分。

2. this指针概念

class Student 
{
public:
    Student(int age)
    {
        this->age = age;  //this指针用于将类的成员变量age与有参构造函数的形参age进行区分
    }

    Student& AgeAdd(Student stu1)   //Student&返回的是对象本身,Student则返回的是值(值传递会调用拷贝构造函数,返回的是值的拷贝)
    {
        this->age += stu1.age;
        return *this;  //返回对象本身
    }

    int age;
};


//构造函数调用
void test1()
{
    Student stu1(10);
    Student stu2(10);
    //链式编程思想
    stu2.AgeAdd(stu1).AgeAdd(stu1).AgeAdd(stu1).AgeAdd(stu1)
}

3. 空指针访问成员函数

class Student 
{
public:
    void func1()
    {
        cout << "print 0" << endl;
    }
    void func2()
    {
        //因为我们创建的指针是一个空指针,因此无法指向特定的对象
        //为了解决这种情况,可以加入如下代码段增加代码的鲁棒性
        if (this == NULL)
        {
            return;
        }
        cout << "age is : " << age << endl;  //这句话等价于cout << "age is : " << this->age << endl;
    }

    int age;
};


//构造函数调用
void test1()
{
    Student* p = NULL;
    p->func1();
    p->func2();  //报错
}

int main()
{
    test1();
    return 0;
}

 

 

4. const修饰成员函数

class Student 
{
public:
    //每个成员函数默认都有this指针,他是一个指针常量,指针指向的方向不可改变
    //函数后面加const,是对this指针进行修饰,this指针变成一个常量指针常量,其值和指向均不可进行修改
    void func1() const
    {
        this->height = 180;  //height前面加了mutable,是一个特殊的成员变量,可以对其值进行修改。
        this->age = 20;     //报错,age是普通成员函数,在常函数中不可对其值进行修改
    }
   
    void func2()
    {
    }

    int age;
    mutable int height;
};


//构造函数调用
void test1()
{
    const Student stu1; //创建常对象
    stu1.age = 10; //报错
    stu1.height = 167; //height可修改

    stu1.func1();  //可执行,func1()为常函数,可以被常对象调用
    stu1.func2(); //报错,func2()并非常函数

}

int main()
{
    test1();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值