构造函数、析构函数、虚函数、纯虚函数、虚析构函数、拷贝构造、protected、private、public

1,构造函数

定义:类的对象初始化时调用、为了实现类对象初始化赋值的功能,构造函数不能是虚函数。

特点:基类的构造函数是不能继承的,如果初始化子类时先调用基类的构造函数再调用子类的构造函数。

用法:在类中声明一个和类名相同的函数。

2,析构函数

定义:析构函数和构造函数对应,对象释放时调用、为了释放对象占有的内存空间。

特点:析构函数应当是虚函数,除非不用做基类。当初始化子类后程序结束释放资源的时候先是调用子类的析构函数并不会调用基类的析构函数,除非基类将该函数定义为虚析构函数。

用法:和构造函数类似在前添加一个"~"即可。

以下方法主要针对于"继承"

3,虚函数

介绍:用来实现多态,允许基类的函数被实例化。通过在基类中声明虚函数,并在派生类中进行重写,实现了在运行时根据对象的实际类型选择合适的函数实现。

实现方式:在函数头前通过关键字“virtual”来实现。

class A  
{  
public:  
    virtual void foo()  
    {  
        cout<<"A::foo() is called"<foo();   // 在这里,a虽然是指向A的指针,但是被调用的函数(foo)却是B的!  
    return 0;  
}

总结:虚在所谓“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。

静态/动态多态:动态多态就是虚函数来实现的,静态多态如重载在编译时即可确定,但是动态多态在编译时不能被确定。

        虚函数只能借助于指针或者引用来达到多态的效果。

参考:虚函数和纯虚函数的区别_hackbuteer1的博客-CSDN博客

4,纯虚函数

定义:1、在很多情况下,虚函数基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

实现方式:在基类中实现纯虚函数的方法是在函数原型后加“=0”。

介绍:编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象,并且要求派生类必须重写该函数。

#include 
using namespace std;

class B
{
public:
        B(){std::cout << "B构造"<W();
        
        delete b;

   return 0;
}

5,虚析构函数

        作用:通常子类定义后程序执行完毕,程序会调用子类的析构函数,而不会调用基类的析构函数,会造成内存泄漏。为了解决上述问题需要将基类相应的对象定义为虚析构函数,如果指针指向的是子类对象,在程序释放时,程序将调用子类的析构函数外,还会自动调用基类的析构函数,将基类内存同时释放。对于抽象类一定要在基类中将对象定义为虚析构函数。

        实现方式:在析构函数前填写虚函数关键字virtual。

#include 

struct A1 {
    ~A1() { std::cout << "~A1()" << std::endl; }
};
struct B1 : public A1 {
    char* data;
    B1() : data(new char[16]) {}
    ~B1() { std::cout << "~B1()" << std::endl; delete[] data; }
};

struct A2 {
    virtual ~A2() { std::cout << "~A2()" << std::endl; }
};
struct B2 : public A2 {
    char* data;
    B2() : data(new char[16]) {}
    virtual ~B2() { std::cout << "~B2()" << std::endl; delete[] data; }
};

int main() {
    
    A1* p1 = new B1; delete p1; std::cout << std::endl;
    A2* p2 = new B2; delete p2; std::cout << std::endl;
    return 0;
    
}

输出:

~A1()

~B2()
~A2()

6、拷贝(复制)构造函数

定义:将一个对象复制到新创建的对象中。

何时调用:新建一个对象并将其初始化为同类现有对象时,复制构造将会被调用。

注意:如果新new一个对象只调用其构造函数,如果将其赋给其他对象则会调用拷贝构造函数。和构造函数相同,拷贝构造函数如果类里面没有定义任何拷贝构造函数时,编译器会提供一个默认的拷贝构造函数,进行成员变量之间的拷贝。

#include 
using namespace std;

class B
{
public:
        int *a = new int(6);
        
        B(){std::cout << "B构造"<a) << std::endl;

        B b2 = *b1;
        cout << "222:"<< *(b2.a) << std::endl;

        *(b1->a) = 7;
        cout << "333:"<< *(b1->a) << std::endl;
        
        cout << "444:"<< *(b2.a) << std::endl;
                
        // delete b1;
        cout << "Hello World" << std::endl;
   return 0;
}

为了解决函数内变量被修改的问题。

传值会调用copy 构造函数,形式参数是实参的别名,操作形参相当于操作实参(引用的背部机制就是指针),copy的意思是实参引用占据的资源(内存,句柄)需要在新生成的形式参数中重新开辟一份(这是需要copy构造函数来实现的),传右值引用(&&)会调用move构造函数,move的意思是将实参占有的资源(内存 句柄)的占有权传递给形式参数,自己不再拥有占有权,不重新分配新的资源。
链接:https://zhuanlan.zhihu.com/p/373960685

6.1、深拷贝和浅拷贝

定义:深拷贝指的是拷贝者在拷贝过程中将拷贝对象的地址赋值给拷贝者。当数据成员中有指针时,必须要用深拷贝。潜拷贝指的是拷贝者在拷贝过程中将拷贝对象的值赋值给拷贝者。在这个过程中拷贝者会new出新的空间进行存储。

使用:在将对象赋值给相同类型的对象时,会默认调用拷贝构造函数,如果函数没有定义拷贝构造函数则调用编译器默认的拷贝构造函数,这个时候为潜拷贝,拷贝对象的值改变时拷贝者对应的变量值也会改变。如果函数重新定义了拷贝构造函数则编译器调用新的拷贝构造函数,这个时候为深拷贝,拷贝对象的值改变时拷贝者对应的变量值不会改变

C++——深拷贝和浅拷贝_c++深拷贝和浅拷贝_Verdure的博客-CSDN博客

7、protected、private、public

一句话概括,private只有在类内中可以使用,protected在子类中也可以使用在类外不能,private在类外中可以使用。

你可能感兴趣的:(C++,c++,开发语言)