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