zoukankan      html  css  js  c++  java
  • C++ 赋值构造函数 拷贝构造函数 析构函数

    1.拷贝构造函数

    用类本身的一个已经创建的对象来构造一个新的类对象,新的对象的各个成员的值与传入的对象的成员值一致。

    在创建一个类的时候如果没有显示的为类创造拷贝构造函数,系统会默认生成一个。

    class Person{

    public:

      string Name;

    }

    Person p;

    p.Name = "test";

    Person p1(p);//这里即使我们在类中没有写这个函数也可以直接使用,系统会默认帮我们生成一个

    cout<<p1.Name<<endl;

    默认生成的拷贝构造函数如下:

    class Person{

    public:

      string Name;

      Person(const Person &p){

        Name = p.Name;  

      }

      //Person(const Person &p):Name(p.Name){}//简写方式

    }

    2.赋值构造函数

    同上面拷贝构造函数类似,在创建一个类的时候如果没有显示地创建,系统会默认生成。

    我感觉赋值构造函数可以理解成重载了操作符“=”

    class Person1
    {
    private:
    public:
       std::string Name;
    };
    int main(int argc, char *argv[])
    {
       Person1 p;
       p.Name = "stes";
       Person1 p1=p;//这里我们在类中没有显示指明操作符“="的使用,这里直接就可以用
       cout<<p1.Name<<endl;
    }

    默认生成的赋值构造函数如下:

    class Person1
    {
    private:
    public:
       std::string Name;

       Person1 &operator=(const Person1 &p)
       {
           Name = p.Name;
           return *this;
       }
    };

    3.析构函数

    类中如果我们没有显示定义析构函数,系统会默认帮我们生成一个默认的析构函数。

    析构函数本身并不直接销毁成员,成员是在析构函数体之后隐含的析构阶段中被销毁的。

    就如我们显示的为Person定义一个析构函数~Person(){},这个析构函数体中什么代码都没有写,但还是完成了成员的销毁工作,因为这项工作并不是在析构函数的函数体中完成的,而是在执行完析构函数后隐含的析构阶段销毁的。

    注意在这个析构阶段并不会销毁使用动态内存分配的对象,所以如果我们在构造函数中使用了动态内存来创建成员,那么在析构函数中我们需要手动的delete这个成员。如下:

    class Person
    {
    private:
    public:
        std::string* Name;
        Person(std::string name)
        {
            Name = new string(name);
        }
        ~Person()
        {
            delete Name;
        }
    };
    一般需要写析构函数的类中都需要我们手动拷贝构造函数和赋值构造函数:
    如上面生成的Person类,我们像下面这样使用时会报错:
    Person p("tom");
    Person p1(p);
    cout<<p1.Name<<endl;
    这里报错的地方是在程序调用结束后回收p和p1掉析构函数的时候,原因是默认的拷贝构造会让p1的Name指向p的Name,也就是p的Name开辟的动态内存有两个指针指向它,而p和p1销毁的时候都调用了delete Name,就会产生对一个内存释放两次的操作,后一次的delete必然报错,因为它已经找不到那块内存了。
    需要手动添加一个拷贝构造函数如下:就不会报错了:
    class Person
    {
    private:
    public:
        std::string* Name;
        Person(std::string name)
        {
            Name = new string(name);
        }
        Person(const Person &p){
            Name= new string(*p.Name);//这里就不是直接让Name指向p的Name,而是重新开一块新的动态内存,析构的时候各自释放各自的就没问题了
        }
        ~Person()
        {
            delete Name;
        }
    };
    赋值构造函数中需要delete掉原来Name所指向的数据,不然赋值的时候将Name指针指向了别的动态内存,原来指针指向的内存就没法释放。
    Person& operator=(const Person &p){
      auto s = new string(*p.Name);
      delete Name;
      Name = s;            
    }
     
    4.=default和=delete的使用
    将一个函数定义为=default,就是告诉编译器让它使用默认生成的该函数,当然这个只能用于哪些编译器会默认生成的函数上面,如空构造函数,拷贝构造函数,拷贝赋值运算符:
    class Person
    {
    private:
    public:
        std::string* Name;
        Person()=default;
        Person(const Person& p)= default;
        Person& operator=(const Person& p)=default;
        ~Person()=default;
    };
    将一个函数定义为=delete表示删除该函数,外面就不能以任何的方式访问到它,不能删除一个析构函数:
    class Person
    {
    private:
    public:
        std::string* Name;
        Person()=delete;
        Person(const Person& p)= delete;
        Person& operator=(const Person& p)=delete;
    };
    如果一个类中有成员不能默认构造、拷贝或者赋值或者销毁,则对应的成员函数系统默认会生成delete的。
  • 相关阅读:
    @PathVariable和@RequestParam的区别,@SessionAttributes
    forward和redirect的区别
    JSP页面的静态包含和动态包含
    ConcurrentHashMap源码解析
    Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX
    最小生成树
    tomcat启动项目内存溢出问题
    强引用,弱引用,4种Java引用浅解(涉及jvm垃圾回收)
    CXF 在WAS上报Unmarshalling Error的问题
    CXF处理Date类型的俩种方式
  • 原文地址:https://www.cnblogs.com/maycpou/p/14734549.html
Copyright © 2011-2022 走看看