c++3(c++2的补充)

1深拷贝和浅拷贝(编译器自带浅拷贝)

 

其实听名字也知道,拷贝拷贝,就是复制过来,一模一样赋值,

1. 使用一个已经创建完毕的对象来初始化一个新对象

 

Person man(100); //p对象已经创建完毕
    Person newman(man); //调用拷贝构造函数

//2. 值传递的方式给函数参数传值 //相当于Person p1 = p;

作为参数的时候(我们知道作为参数的时候其实本质也是复制一份传递过去,所以经常发现如果没有地址符&你就算怎样改变形参参数的值,本质也不会更改)

 

//3. 以值方式返回局部对象 P

//3. 以值方式返回局部对象
Person doWork2()
{
    Person p1;
    cout << (int *)&p1 << endl;
    return p1;
}

 

//拷贝构造函数
    Person(const Person& p) {
        age = p.age;
        cout << "拷贝构造函数!" << endl;
    }

所以我们就可以这样操作

Person p2(p1);

但是要清楚

//如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
    Person p1; //此时如果用户自己没有提供默认构造,会出错
    Person p2(10); //用户提供的有参
    Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供

    //如果用户提供拷贝构造,编译器不会提供其他构造函数
    Person p4; //此时如果用户自己没有提供默认构造,会出错
    Person p5(10); //此时如果用户自己没有提供有参,会出错
    Person p6(p5); //用户自己提供拷贝构造

这是拷贝的基础知识,那么什么是深浅拷贝呢

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

浅拷贝其实看上面的基础就大概差不多是浅拷贝一些应用,

主要看看深拷贝的情况,如指针,我们知道,指针实质上就是一个局部变量在堆区,但是用指针的时候需要分配内存空间需要new这时候就在栈区了(由程序员自己控制的区)

如下

//拷贝构造函数  
    Person(const Person& p) {
        cout << "拷贝构造函数!" << endl;
        //如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
        m_age = p.m_age;
        m_height = new int(*p.m_height);
        
    }

    //析构函数
    ~Person() {
        cout << "析构函数!" << endl;
        if (m_height != NULL)
        {
            delete m_height;
        }
    }
public:
    int m_age;
    int* m_height;
};

 

 

 

如果不new一个空间深拷贝的话,那看看浅拷贝(即编译器自带的)会干嘛,本质上是:

 

 

 指针由于是拷贝,指向的地址也是一样,造成析构函数释放的时候,前面一个析构释放了,后面一个又释放

所以这就是浅拷贝带来的问题,堆区的内存重复释放

2 this指针

this一般就是指所在的类,用途是

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *thiclass Person
{
public:

    Person(int age)
    {
        //1、当形参和成员变量同名时,可用this指针来区分
        this->age = age;
    }
    //返回的本体则需要引用的方式返回
    Person& PersonAddPerson(Person p)
    {
        this->age += p.age;
        //返回对象本身
    //在这里this作为值返回时因为本身就是一个指针,再加*即*this那么意义就是本体的意思了即下面的p2
return *this; } int age; }; void test01() { Person p1(10); cout << "p1.age = " << p1.age << endl; Person p2(10); p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1); cout << "p2.age = " << p2.age << endl; } int main() { test01(); system("pause"); return 0; }

 

 

 

 3动态多态

这里需要一个关键字virtual

为什么是virtual,函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。

看下面的代码

class Animal
{
public:
    //Speak函数就是虚函数
    //函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。
    virtual void speak()
    {
        cout << "动物在说话" << endl;
    }
};

class Cat :public Animal
{
public:
!!!!!!!// 这里可加可不加virtual 结果一样!!!!!!!!!!!!!!
void speak() { 
cout
<< "小猫在说话" << endl;
}
};


class Dog :public Animal {
public:
  !!!!!!!// 这里可加可不加virtual 结果一样!!!!!!!!!!!!!!
void speak() { cout << "小狗在说话" << endl; } }; //我们希望传入什么对象,那么就调用什么对象的函数 //如果函数地址在编译阶段就能确定,那么静态联编 //如果函数地址在运行阶段才能确定,就是动态联编 void DoSpeak(Animal & animal) { animal.speak(); } // //多态满足条件: //1、有继承关系 //2、子类重写父类中的虚函数 //多态使用: //父类指针或引用指向子类对象 void test01() { Cat cat; DoSpeak(cat); Dog dog; DoSpeak(dog); } int main() { test01(); system("pause"); return 0; }

首先在c++中。。。。父类的引用或指针   。。。。。就是可以代表一家之主的意思当然可以代表这个家的地址,有了家的地址当然可以用家中的子类,父子其实就是一体。

void DoSpeak(Animal & animal)
DoSpeak(cat);
DoSpeak(dog);
 

要想让猫为参数猫说话,让狗狗说话就需要地址在编译时没有绑定,所以不加virtual的话,不管传什么都会是调用Animal的spreak结果都会是动物在说话

所以要在编译前不绑定,即晚绑定,加virtual。那么这个原理是什么

virtual

 

posted @ 2021-01-03 14:04  To_Yang  阅读(156)  评论(0编辑  收藏  举报