精华内容
下载资源
问答
  • 构造函数析构函数

    2016-02-16 14:46:56
    构造函数是一个与类同名的方法,可以没有参数,有一个参数多个参数,但是构造函数没有返回值。如果构造函数没有参数,该函数被称为类的默认构造函数。 (1) 一个类可以包含多个构造函数,各个构造函数之间通过...
  • 构造函数在定义时可以有参数或没有参数 没有任何返回类型的声明 析构函数 C++中的类可以定义一个特殊的成员函数清理对象,叫析构函数 语法:~calssname() 析构函数没有任何参数和返回类型的声明 析构函数对象销毁...

    构造函数
    C++中的类可以定义与类名相同的特殊成员函数,这种函数叫构造函数
    构造函数在定义时可以有参数或没有参数
    没有任何返回类型的声明

    析构函数
    C++中的类可以定义一个特殊的成员函数清理对象,叫析构函数
    语法:~calssname()
    析构函数没有任何参数和返回类型的声明
    析构函数在对象销毁时自动被调用

    class test{
    public:
    	test(){
    		a=10;
    		p=(char *)malloc(100);
    		strcpy(p, "abc");
    		cout<<"构造函数 被调用"<<endl; 
    	}
    	~test(){
    		if (p!=NULL){
    			free(p);
    		}
    		cout<<"析构函数 被调用"<<endl;
    	}
    	
    private:
    	int a;
    	char *p;
    };
    
    void play(){
    	test t1, t2;	//先创建的后释放 
    }
    int main(){
    	play();
    	return 0;
    } 
    

    构造函数的分类

    默认无参构造函数:当类中没有定义构造函数时,会默认提供一个无参构造函数,函数体为空
    默认拷贝构造函数:当类中没有定义拷贝构造函数,默认提供一个拷贝构造函数,简单的进行成员变量的赋值

    class test{
    public:
    	test(){		无参构造函数 
    		m_a=1;
    		m_b=2;
    	}
    	test(int a, int b){		有参构造函数 
    		m_a=a;
    		m_b=b;
    	} 
    	test(const test& obj){	拷贝构造函数 
    		
    	} 
    private:
    	int m_a, m_b;
    }; 
    
    int main(){
    	调用无参构造函数
    	test t1;		
    	
    	调用有参构造函数
    	test t2();		错误 
    	test t2(1, 2);	正确 
    	
    	对象的初始化和对象的赋值是两个不同的概念
    	t1=t2;	把t2赋值给t1 
    } 
    

    调用拷贝构造函数

    class test{
    public:
    	test(){
    		m_a=1;
    		m_b=2;
    	} 
    	test(int a, int b){
    		m_a=a;
    		m_b=b;
    	}
    	void get(int a, int b){
    		m_a=a;
    		m_b=b;
    	}
    	test(const test& obj){
    		m_a=obj.m_a;
    		m_b=obj.m_b;
    	} 
    	~test(){
    		
    	}
    private:
    	int m_a, m_b;
    };
    
    void func(test p){
    	p.get(1, 2);
    } 
    
    test f(){
    	test t(1, 2);
    	return t;	用t返回一个匿名对象,调用匿名对象的拷贝构造函数,然后执行t的析构函数 
    }
    int main(){
    	test t1(1, 2);
    	test t0(1, 2);
    	t0=t1;	用t1给t0赋值,不调用拷贝构造函数 
    	
    	调用t2的拷贝构造函数 
    	test t2=t1; 
    	
    	调用t3的拷贝构造函数
    	test t3(t1); 
    	
    	t4实参取代p形参,会调用p的拷贝构造函数初始化p 
    	func(t1);
    	
    	因为f函数的返回值没有变量接,所以调用这个匿名对象的析构函数 
    	f();
    	
    	用匿名对象初始化t4, 把匿名对象转换成t4
    	test t4=f();
    	
    	如果把f()赋值给一个初始化了的对象,则匿名对象被析构掉 
    	t1=f();	匿名对象被析构掉 
    }
    

    构造函数调用规则
    当类中定义了构造函数,C++编译器就不会提供无参构造函数
    所以只要写了构造函数,就必须要用(可以写多个,用一个)

    深拷贝和浅拷贝

    class test{
    public:
    	test(const char *myp){
    		len=strlen(myp);
    		p=(char *)malloc(len+1);
    		strcpy(p, myp); 
    	}
    	test(const test& obj){
    		len=strlen(obj.len);
    		p=(char *)malloc(len+1);
    		strcpy(p, obj.p);
    	} 
    	~test(){
    		if (p!=NULL){
    			free(p);
    			p=NULL;
    			len=0;
    		}
    	} 
    private:
    	char *p;
    	int len;
    }; 
    int main(){
    	//浅拷贝 
    	test t1("abcde");	在堆区为t1的p开辟内存空间
    	test t2=t1;
    	如果默认执行C++提供的拷贝构造函数,只是进行变量的简单赋值,将t2的p指向t1中的p指向的地址
    	后面的对象先析构,将t2中的p析构,之后析构t1中的p,但此时内存已经被析构掉,程序崩溃(野指针)
    	 
    	深拷贝	
    	如果调用自己编写的拷贝构造函数,两次析构的不是同一块内存,不会出错
    	
    	
    	注意对象的赋值也是浅拷贝,需要重载=运算符	 
    }
    

    对象初始化列表
    可以在初始化列表对成员变量进行初始化,
    也可以在拷贝构造函数中通过初始化列表初始化变量

    class A{
    public:
    	A(int var){
    		a=var;
    	}
    private:
    	int a;
    };
    
    class B{
    public:
    	B(int var_b1, int var_b2, int var_a1, int var_a2):a1(var_a1), a2(var_a2), c(0){
    		b1=var_b1;
    		b2=var_b2;
    	}
    
    private:
    	int b1;
    	int b2;
    	A a1;
    	A a2;
    	const int c; 
    };
    
    int main(){
    	A(1);
    	B(1, 2, 3, 4);
    
    	return 0;
    }
    
    
    先执行组合对象的构造函数
    如果组合对象有多个,按照 定义 的顺序,而不是按照初始化列表的顺序。
    被组合对象的构造顺序和定义顺序有关系,与初始化列表顺序没有关系
    析构函数的执行顺序和构造函数的调用顺序相反	
    
    展开全文
  • 构造函数构造函数负责创建对象和初始化对象,在用...构造函数和类的名字一样并且没有返回值,可以有参数也可以没有参数,也可以有默认值。主要是 为数据赋初值。构造函数可以重载。 构造函数的初始化列表: ...

    构造函数:

    构造函数负责创建对象和初始化对象,在用默认的构造函数创建对象时,如果创建的是全局对象或
    静态对象,则对象的值全为0,否则对象的值是随机的,和C语言中定义的变量一样(类也是一
    个类型)。建立对象的同时会自动调用构造函数。
    
    构造函数的规则:
    构造函数和类的名字一样并且没有返回值,可以有参数也可以没有参数,也可以有默认值。主要是
    为数据赋初值。构造函数可以重载。
    构造函数的初始化列表:
    构造函数的初始化列表中的数据的初始化顺序和声明的相同。
    const数据成员必须使用初始化列表进行初始化。
    
    这里写代码片
    class Student
    {
        public:
            Student(int m_id = 0,int m_score = 0);//:id(m_id),score(m_score);
            //这里的默认参数不能少,因为下面的main函数中定义了一个对象没有赋初值
            ~Student();        
            Student(const Student& pt);
    
            int getid(void);
            int getscore(void);
            static int getnum(void);
    
        private:
            int id;
            int score;
            static int num;     
    };
    Student::Student(int m_id ,int m_score ):id(m_id),score(m_score)
        {
            num++;  
            id = m_id;
            score = m_score;
        }
        注意:构造函数的初始化列表要和构造函数的实体写在一起,不能写到声明里面去。
        默认参数在实体或声明中都行
    
        public:
            Student(int m_id  ,int m_score  );//:id(m_id),score(m_score);
            ~Student();        
            Student(const Student& pt);
    
        Student::Student(int m_id = 0,int m_score = 0):id(m_id),score(m_score)
        {
            ...
        }
        注意:默认参数要么在声明里(第一种)要么在函数的实体里的形参里(第二种),两者
        是或的关系。而初始化列表必须和函数的实体在一起,要么不用初始化列表。

    析构函数:

    一个类可能在构造函数里分配资源也就是内存(如果有数据的话),这些资源需要在对象不复存在
    之前被释放,也就是这个对象的生命周期结束的时候,由系统自动调用,,析构函数没有返回值类
    型,没有参数,也没有重载。如果没有自定义析构函数,则编译器提供一个默认的析构函数。
    

    拷贝构造函数:

        Student(const student&)//拷贝构造函数的形式
        拷贝函数具有单个形参,并且这个形参是对该类型的引用。
        1.当定义一个新对象并用同一个类型的对象对它初始化时,将显示调用拷贝构造函数。比如:
        Student s1("joe");
        Student s2 = s1;//这时调用拷贝构造函数
        Student s3("tom");
        s2 = s3;//这时会调用赋值运算符
        2.拷贝一个临时对象时,比如:
        void foo(Student stu);
        foo(s1);//这里会调用拷贝构造函数,传递s1的一个副本,传递副本的时候会调用拷贝
        构造函数
        3.Student bar();
        Student tom = bar();这时会调用拷贝构造函数
    
        什么时候需要拷贝构造函数呢?(有的需要,有的不需要)
        1.当类数据成员有指针时需要定义拷贝构造函数和定义赋值运算符
        2.当类数据成员管理资源,比如打开一个文件,申请一块内存区域...
    
        C++为什么要定义拷贝构造函数?
        这里涉及到深拷贝与浅拷贝的问题(浅拷贝就是两个指针指向同一块资源,深拷贝就是两个
        指针指向两块资源,这两块资源的内容一样),默认的拷贝构造函数是浅拷贝,如果涉及到
        指针,同一块资源会被析构两次,会出现问题。
    
    展开全文
  • 构造函数析构函数 构造函数 每个类都分别定义了他的对象被初始化的方式, 类通过一个几个特殊的成员函数来控制其对象的初始化过程。 构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建, ...

    构造函数与析构函数

    1. 构造函数

      每个类都分别定义了他的对象被初始化的方式, 类通过一个或几个特殊的成员函数来控制其对象的初始化过程。 构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建, 就会执行构造函数。
      构造函数的名字和类名相同。和其他函数不同的是,构造函数没有返回类型;类可以包含多个构造函数,和其他重载函数差不多,不同构造函数之间必须在参数数量或参数类型上有所差别。
      构造函数的名字和类名相同,和其他重载函数差不多,不同的构造函数必须在参数数量或参数类型上有所区别。
      不同于其他的成员函数,构造函数不能被声明成const的。当我们创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其“常量”属性。因此,构造函数在const对象的构造过程中可以向其写值。

      合成的默认构造函数
      我们的类没有显示定义构造函数, 那么编译器就会为我们隐式的定义一个默认构造函数,初始化规则:1、如果存在类内的初始值,用它来初始化成员; 2、否则,默认初始化该成员。

      某些类不能依赖于合成的默认构造函数
      1、只有当类没有声明任何构造函数时,编译器才会自动的生成默认构造函数。
      2、如果类包含有内置类型或者符合类型的成员,则只有当这些成员全部都被赋予了类内的初始值时,这个类才适合于使用合成的默认构造函数。
      (如果定义在块中的内置类型或复合类型(数组或指针)的对象被默认初始化,则他们的值将是未定义的。)
      3、有的时候编译器不能为某些类合成默认的构造函数。例如:如果类中包含一个其他类类型的成员且这个成员的类型没有默认构造函数,那么编译器将无法初始化该成员。
      在 c++11 新标准中, 如果我们需要默认的行为, 那么可以通过在参数列表后面写上=default 来要求编译器生成构造函数。

      Sales_data()=default;

      默认构造函数的作用
      当对象被默认初始化或值初始化时自动执行默认构造函数
      默认初始化:
      1、当我们在块作用域内不使用任何初始值定义一个非静态变量或者数组时。
      2、当一个类本身含有类类型的成员且使用合成的默认构造函数时
      3、当类类型的成员没有在构造函数初始值列表中显示的初始化时。
      值初始化:
      1、在数组初始化的过程中如果我们提供的初始值数量少于数组的大小时。
      2、当我们不使用初始值定义一个局部静态变量时。
      3、当我们通过书写形如 T()的表达式显示的请求值初始化时,其中 T 是类型名。
      类必须包含一个默认构造函数以便在上述情况使用。

    class nodefault{
    public:
        nodefault(string &s);
    };
    struct A{
        nodefault my_mem;
    };
    A a;//错误,不能为A和成构造函数
    struct B{
        B(){}       //错误,b_member没有初始值
        nodefault b_member;
    }
    **构造函数初始值列表**
    ```
    Sales_data(const string&s,unsigned n,double p):Bookno(s),units_sold(n),reve(p*n){}
    ```
    构造函数初始值列表负责为新创建的对象的一个或几个数据成员赋初值。当某个数据成员背构造函数初始值列表忽略时,它将以与合成默认构造函数相同的方式隐式初始化。构造函数不应该轻易覆盖掉类内的初始值, 除非新赋的值与原值不同。如果你不能使用类内初始值, 则所有的构造函数都应该显示的初始化每个内置类型。
    当我们定义变量时习惯于立即对其进行初始化,而非先定义、再赋值:
    
    string foo="hello world";//定义并初始化
    string bar;//默认初始化成空的string
    bar="hello world";//为bar赋一个新值

    就对象的数据成员而言,初始化和赋值也有类似的区别。如果没有在构造函数的初始值列表中显示地初始化成员,则该成员将在构造函数体之前执行默认初始化

    Sales_data::Sales_data(const string &s,unsigned cnt,double price)
    {
        bookno=s;
        units_sold=cnt;
        revenue=cnt*price;
    }

    此版本与原来的区别是这个版本对数据成员执行了赋值操作,原来的版本初始化了他的数据成员。
    构造函数的初始值有时必不可少:有时候我们可以忽略数据成员初始化和赋值之间的差异,但并非总能这样。如果成员是 const 或者是引用的话必须将其初始化。类似的,当成员属于某种类类型且该类型没有定义默认构造函数时,也必须将这个成员初始化。

    class constref{
        public:
            constref(int ii);
        private:
            int i;
            const int ci;
            int &ri;
    };
    constref::constref(int ii)
    {
        i=ii;
        ci=ii;  //错误:不能给const赋值
        ri=ii;  //错误ri 没有被初始化
    }
    //随着构造函数体一开始执行,初始化就完成了

    在很多类中,初始化和赋值的区别事关底层的效率问题:前者直接初始化数据成员,后者则先初始化再赋值,并且一些数据成员必须被初始化。
    成员初始化的顺序
    构造函数初始值列表只用于说明初始化成员的值,而不限定初始化的具体执行顺序。
    成员初始化顺序与他们在类中定义的出现顺序一致,构造函数初始值列表中初始值的前后位置关系不会影响实际的初始化顺序。

    class X{
        int i;
        int j;
    public:
        //错误,i在j之前初始化,未定义的。
        X(int val):j(val),i(j){}
    }

    默认实参和构造函数
    如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数。

    委托构造函数
    C++11新标准扩展了构造函数初始值的功能,使得我们可以定义所谓的委托构造函数。
    一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程, 或者说是它把它自己的一些(或者全部职责)职责委托给了其他构造函数。
    一个委托构造函数也有一个成员初始值的列表和一个函数体。在委托构造函数内,成员初始值列表只有一个唯一的入口,就是类名本身。和其他成员初始值一样,类名后面紧跟圆括号括起来的参数列表,参数列表必须与类中另外的一个构造函数匹配。

    class sales_data{
    public:
    //非委托构造函数使用对应的实参初始化成员
    sales_data(string s,unsigned cnt,double price):bookno(s),units_sold(cnt),reven(cnt*price){}
    //其余构造函数全都委托给另一个构造函数
    sales_data(): sales_data("",0,0){}//定义默认构造函数
    sales_data(string s):sales_data(s,0,0){}
    sales_data(istream &is):sales_data(){read(is,*this)}//先委托给默认构造函数,默认构造函数委托给三参数构造函数
    };

    当一个构造函数委托给另外一个构造函数时,受委托的构造函数的初始值列表和函数体被依次执行。然后控制权才会交还给委托者的构造函数

    隐式的类类型转换(类型转换构造函数)
    如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,有时我们把这种构造函数称作转换构造函数。
    能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则。

    class Sales_data{
    public:
        Sales_data()=default;
        Sales_data(string s,unsigned cnt,double price):bookno(s),units_sold(cnt),reven(cnt*price){}
        Sales_data(string s):bookno(s){}
        Sales_data(istream &is):{read(is,*this)}
    };
    string null_back="9-999-99";
    item.combine(null_book);//在需要Sales_data的地方可以使用string或istream代替
    • 只允许一步类型转换:
      编译器只会自动的执行一步类类型转换。如:
    Item.combine("9-999-99”);//错误, 先把 9-999-99 转换成 string 再将这个 string 转换成sales_data。
    Item.combine(string("9-999-99”));//正确, 先显示转换成 string, 在隐式转换成 sales_data.
    Item.combine(sales_data("9-999-99”));//正确, 隐式转换成 string, 再显示转换成 sales_data。
    • 抑制构造函数定义的隐式转换
      在要求隐式转换的程序上下文中,我们可以通过将构造函数声明为explicit 加以阻止。关键字explicit 只对一个实参的构造函数有效。只能在类内声明构造函数时使用 explicit 关键字,在类外部定义时不能重复。
    class Sales_data{
    public:
        Sales_data()=default;
        sales_data(string s,unsigned cnt,double price):bookno(s),units_sold(cnt),reven(cnt*price){}
        explicit Sales_data(string s):bookno(s){}
        explicit Sales_data(istream &is):{read(is,*this)}
    };
    • explicit构造函数只能用于直接初始化
      发生隐式转换的一种情况是当我们执行拷贝形式的初始化时(使用=)。此时我们只能使用直接初始化而不能使用explicit构造函数:
    Sales_data itm1(null_book);//正确,直接初始化
    Sales_data itm2=null_book;//错误,不能将 explicit 函数用于拷贝形式的初始化过程。
    • 为转换显示地使用构造函数
      尽管编译器不会将explicit的构造函数用于隐式转换过程,但是我们可以使用这样的构造函数显示地强制进行转换:
    Item.combine(sales_data(null_book));//正确:
    Item.combine(static_cast<sales_data>(cin));//正确, 执行了显示的转化

    析构函数
    释放对象使用的资源,并销毁对象的非static数据成员。无参数、无返回值。

    class foo{
    public:
        ~foo();
    };

    析构函数无参数所以也不可以被重载,对于一个给定的类,只会有唯一一个析构函数。

    • 析构函数工作
      如同构造函数有一个初始化部分和一个函数体, 析构函数有一个函数体(通常释放在生存期分配的资源)和一个析构部分。在一个构造函数中,成员的初始化是在函数体执行之前完成的,在一个析构函数中,首先执行函数体,然后销毁成员。成员按照初始化顺序的逆序销毁。
      在一个析构函数中,不存在类似构造函数中初始化列表的东西控制成员如何销毁,析构部分是隐士的。销毁类类型的成员需要执行自己的析构函数。内置类型没有析构函数,销毁内置类型什么也不做。
      NOTE:隐式销毁一个内置指针类型的成员,不会delete它所指向的对象,与普通指针不同,智能指针是类类型,所以具有析构函数。因此,与普通指针不同,智能指针成员在析构阶段会被销毁。

    • 析构函数调用时机
      无论何时一个对象被销毁,就会自动调用其析构函数。
      1、变量离开作用域是被销毁
      2、当一个对象被销毁时,其成员被销毁。
      3、容器被销毁时,元素被销毁
      4、对于动态分配的对象,应用 delete 时被销毁
      5、临时对象,创建他的完整表达式结束时被销毁
      (当指向一个对象的引用或指针离开作用域时,析构函数不会执行)

    {//新作用域
        p和p2指向动态分配的内存
        Sales_data*p=new Sales_data;//p是一个内置指针
        auto p2=make_shared<Sales_data>();//p2是一个shared_ptr
        Sales_data item(*p);//拷贝构造函数将*p拷贝到item中
        vector<Sales_data> vec;//局部对象
        vec.push_back(*p2;)//拷贝p2指向的对象
        delete p;//对p指向的对象执行析构函数
    }
    //退出局部作用域;对item、p2和vec调用析构函数
    //销毁p2会递减其引用计数;如果计数变为0,对象被释放
    //销毁vec会销毁其元素
    • 合成的析构函数
      当一个类未定义自己的析构函数时,编译器会为他定义一个合成析构函数。对于某些类,合成的析构函数被用来阻止该类型对象被销毁。如果不是这种情况,合成析构函数的函数体就为空。
    class Sales_data{
    public:
        ~sales_data(){}
    };
    //空的函数体执行完毕之后,成员会被自动销毁。

    析构函数体自身并不直接销毁成员,成员是在析构函数体之后隐含的析构阶段中被销毁的。在整个对象销毁过程中,析构函数体是作为成员销毁步骤之外的另一部分而进行的

    展开全文
  • 特别的一个类可以有多个构造函数 ,可根据其参数个数的不同参数类型的不同来区分它们,即构造函数的重载。 注意: 构造函数的函数名称与类名同名,其他方法(函数)名称可以自定义。 构造函数没有也不能有返回...

    一、构造函数

    主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们,即构造函数的重载。

    注意:

         构造函数的函数名称与类名同名,其他方法(函数)名称可以自定义。
         构造函数没有也不能有返回类型,而其他函数随意。
         由于构造函数的作用只是在创建对象时用来初始化成员变量和对象的,因此构造函数不能被继承也不能使用接口。
                
         构造函数仅在对象被创建时系统会根据给定的参数以及类中的构造函数定义进行选择调用,
         如果类中没有定义构造函数,系统默认会提供一个无参构造空函数,什么都不会做,只是满足接口要求,构造函数不能被显式调用。其他函数根据程序员需要而调用,且必须显式调用。

         全局对象和静态对象的构造函数在main()函数执行之前就被调用,局部静态对象的构造函数是当程序第一次执行到相应语句时才被调用。然而给出一个外部对象的引用性声明时,并不调用相应的构造函数,因为这个外部对象只是引用在其他地方声明的对象,并没有真正地创建一个对象。

     

    二、拷贝构造函数

    也称为“复制构造函数”。它是一种特殊的构造函数,它在创建对象时,是使用同一类中已创建的对象来初始化新创建的对象。其形参必须是引用,一般用const修饰,但并不限制为const。
                    
           如果用户自己未定义复制构造函数,则编译系统会自动提供一个默认的 复制构造函数,其作用只是简单地复制类中每个数据成员。
                    
           拷贝构造函数通常用于:
                  1. 通过使用另一个同类型的对象来 初始化 新创建的对象。
                  2. 复制对象把它作为 函数参数 传递给函数。
                  3. 复制对象把它作为 函数返回值 并从函数返回这个对象。

    接下来看一个实例:

    #include <iostream>
    using namespace std;
    
    class Point{
        public:
    	    Point(int _x = 0);         //拷贝函数
    	    Point(const Point &p);     //拷贝构造函数
    	    int getX() const { return x; }
    
        private:
    	    int x;
    };
    
    //拷贝函数定义
    Point::Point(int _x) 
    {
    	this->x = _x; 
    }
    //拷贝构造函数定义
    Point::Point(const Point &p) 
    {
    	cout << "Calling the copy constructor" << endl;
    	x = p.x;
    }
    //形参为Point类对象 的函数
    void fun1(Point p)
    {
    	cout << p.getX() << endl;
    }
    //返回值为Point类对象 的函数
    Point fun2()
    {
    	Point tmp(1);
    	return tmp;     
    }
    
    int main()
    {
        Point a(10);
    	Point b(a);    // 用a去初始化b,第一次调用拷贝构造函数
    	cout << b.getX() << endl;													//10
    	fun1(b);    // 对象b作为fun1的实参,第二次调用拷贝构造函数						//10
    	b = fun2();    // 函数的返回值是类对象,函数返回时,第三次调用拷贝构造函数
    	cout << b.getX() << endl;													//1
    }
    

    在了解了拷贝构造函数ed基本知识后,接下来说说深拷贝和浅拷贝:

           1. 深拷贝
                   在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间。
                   在完成深拷贝之后,两个对象的动态成员各指向不同的内存空间,但它们指向的空间具有相同的内容。
                 【有关深浅拷贝的详细分析可移步https://blog.csdn.net/lwbeyond/article/details/6202256】
                    
           2. 浅拷贝
                   指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。
                   但是一旦对象存在了动态成员,在完成浅拷贝之后,销毁对象时,两个对象的析构函数将对同一个内存空间释放两次,会出错。此时只能使用深拷贝。
                   在完成浅拷贝之后,两个对象的动态成员均指向相同的内存空间,共享该空间中的内容。
                
           3. 如何判别一个函数是拷贝构造函数:
                   对于一个类X, 如果一个构造函数的第一个参数是下列之一:
                            a) X&
                            b) const X&
                            c) volatile X&
                            d) const volatile X&
                   且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数。
                   实例:
                            X::X(const X&);  //是拷贝构造函数    
                            X::X(X&, int=1); //是拷贝构造函数   
                            X::X(X&, int a=1, int b=2); //当然也是拷贝构造函数

     

    三、析构函数

    析构函数与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数,自动的回收该对象占有的资源,或是完成一些清理工作。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。
                
           注意:析构函数既没有返回值也没有函数参数,因此析构函数不能被重载。[构造函数可以重载]
                
          析构函数被显示的调用的情况:
                 若对象是静态的,存在堆区分配,当程序未结束且我们想释放堆时,就可以显示的调用析构函数来完成。
                 如果显式地调用了析构函数,此时析构函数和普通成员函数是一样的,并不会造成对象被销毁。

     

    四、综合运用

    有了构造函数和析构函数的基本概念之后,我们来看一个有关【使用构造和析构观察不同对象的生命周期】的综合实例

    #include <iostream>
    using namespace std;
    
    #define MAX 3
    class Array{
    	public:
    		Array(int len=MAX){		//构造函数
    			size = len;
    			addr = new int[len];
    			cout << this << "; general: size = " << size << endl;
    		}
    
    		Array(const Array &obj){	//拷贝构造函数
    		    size = obj.size;
    			addr = new int[size];
    			for(int i = 0; i < size; i++)
    				addr[i] = obj.addr[i];
    			cout << this << "; copy:  size = " << size << endl;
    		}
    
    		~Array(){		//析构函数
    			if(addr){	
    				delete [] addr;
    				addr = NULL;
    			}
    			cout << this << " destructor; size = " << size << endl;
    		}
    
    	private:
    		int *addr;
    		int size;
    };
    
    Array a(1); //全局静态对象,main运行前就存在(先于main构造),进程结束消失(main退出后自动析构)
    
    void test()
    {
    	Array a(2); //动态对象,test调用产生(构造自动被调用),test结束后销毁(析构自动调用)
    
    	static Array b = a; //局部静态对象,不论调用test多少次,都只构造一次,进程结束后消失(main退出后自动析构)
    
    	Array *p = new Array(3); //动态堆对象,new成功时对象产生,构造被自动调用
    	delete p; //delete销毁对象,析构被自动调用。若不delete,则对象会一直在,析构也不被自动调用。
    }
    
    int main()
    {
    	cout << "-----------1--------------" << endl;
    	test();
    	test();
    	cout << "-----------2--------------" << endl;
    }	
    
    
    /**执行结果
    	    0x804a0f8; general: size = 1
    		-----------1--------------
    		0xbfef0a44; general: size = 2
    		0x804a110; copy:  size = 2
    		0x9938038; general: size = 3
    		0x9938038 destructor; size = 3
    		0xbfef0a44 destructor; size = 2
    		0xbfef0a44; general: size = 2
    		0x9938038; general: size = 3
    		0x9938038 destructor; size = 3
    		0xbfef0a44 destructor; size = 2
    		-----------2--------------
    		0x804a110 destructor; size = 2
    		0x804a0f8 destructor; size = 1
    **/

     

    展开全文
  • 特别的一个类可以有多个构造函数 ,可根据其参数个数的不同参数类型的不同来区分它们 即构造函数的重载。 构造函数是一个特殊的成员函数: 1.构造函数的名字必须与类名相同 2.构造函数没有返回值,即使void也
  • 4、定义了类没有定义构造函数时C++会提供默认构造函数,默认构造函数是无参构造函数,仅负责创建对象。 5、一个类可以有多个构造函数。 6、构造函数可以是虚函数。原因:虚函数通过基类指针引用来调用派生类的...
  • 1 构造函数不能为虚函数,原因主要如下 虚函数的调用需要虚函数表指针,而该指针存放在对象...首先析构函数可以为虚函数,而且当要使用基类指针引用调用子类时,最好将基类的析构函数声明为虚函数,否则可以存在内存
  • 一个类可以有多个构造函数 ,可根据其参数个数的不同参数类型的不同来区分它们即构造函数的重载。 特性: (1)函数名与类名相同 (2)没有返回值 (3)构造函数不能被直接调用,必须在创建对象时才会自动调用...
  • 1. 为什么构造函数不能为虚函数?  虚函数的调用需要虚函数表指针,而该指针存放在对象的内容空间中;若构造函数声明为虚函数,... 首先析构函数可以为虚函数,而且当要使用基类指针引用调用子类时,最好将基
  • 1. 为什么构造函数不能为虚函数?  虚函数的调用需要虚函数表指针,而该指针存放在对象的内容空间中;... 首先析构函数可以为虚函数,而且当要使用基类指针引用调用子类时,最好将基类的析构函数声明为虚
  • 【C++】构造函数&&析构函数

    千次阅读 2016-10-06 16:48:02
    构造函数每个类都分别定义了自己的对象被初始化的方式,类通过一个几个特殊的成员函数来控制其对象的初始化过程,这样的函数叫做构造函数构造函数(Constructor)是一种特殊的成员函数,它的名字和类名相同,...
  • 特别的一个类可以有多个构造函数 ,可根据其参数个数的不同参数类型的不同来区分它们 即构造函数的重载。 1.构造函数的命名必须和类名完全相同。在java中普通函数可以构造函数同名,但是必须带有返回值; 2.构造...
  • 析构函数对象超出作用范围使用delete运算符释放对象时被调用,用于释放对象占用的空间。 如果用户没有显式地提供析构函数,系统将提供默认的析构函数析构函数也是以类名作为函数名,与构造函数不同的是,在...
  • 这个问题来自于《Effective C++》条款9:永远不要在构造函数或析构函数中调用虚函数 。 简要结论: 1. 从语法上讲,调用完全没有问题。 2. 但是从效果上看,往往不能达到需要的目的。 Effective 的解释是: 派生...
  • 1、构造函数:对对象进行初始化;析构函数:对对象进行销毁,释放内存。 ...3、实例:定义一个学生类,用构造函数进行初始化,打印出学生信息,最后用析构函数销毁对象。 /***构造函数和析构函数**
  • C++中一般创建对象,拷贝赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法。下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它...
  • 这个问题来自于《Effective C++》条款9:永远不要在构造函数或析构函数中调用虚函数 。 简要结论:  1. 从语法上讲,调用完全没有问题。  2. 但是从效果上看,往往不能达到需要的目的。  Effective 的解释是:...
  • 构造函数和析构函数是否可以是虚...首先析构函数可以为虚函数,而且当要使用基类指针引用调用子类时,最好将基类的析构函数声明为虚函数,否则可以存在内存泄露的问题。 举例说明: 子类B继承自基类A;A *p = new B
  • 析构函数和虚函数的用法和作用?

    千次阅读 2012-07-05 00:01:45
    析构函数也是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载。只是在类对象生命期结束的时候,由系统自动调用释放在构造函数中分配的资源。这种在运行时,能依据其类型确认调用那个函数的...
  • 构造函数和析构函数

    2020-10-29 19:09:27
    每个类都分别定义了它的对象被初始化的方式,类通过一个几个特殊的成员函数来控制其对象的初始化过程,这些函数叫构造函数构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造...
  • 1.类的构造函数和析构函数 类的构造函数:函数名与类名相同,没有...注意:由于在构造函数中会初始化一些类的成员指针,在析构函数中删除,因此在不同的构造函数中给类的成员指针分配内存时,一定要统一用newnew[]
  • **析构函数:**对象销毁前自动调用,执行一些清理工作 当我们不提供构造和析构时,编译器会提供,而编译器提供的时空实现的 构造函数语法:类名(){} 无返回值也不写void 函数名称与类名相同 可以有参数,因此可以
  • 同样,这种情况下,默认构造函数,默认赋值操作符以及默认析构函数可以了。 下面是一些关于构造函数的一些通用规则: 1. 如果默认拷贝构造函数可以处理,则不需要自定义拷贝构造 如果对象没有动态分配的资源,...
  • 静态数据成员在类声明中声明,在包含类方法的文件中初始化。这是因为声明描述了如何分配内存,并不分配内存。初始化时使用作用域解析符来指出...默认析构函数;地址操作符;复制构造函数用于将一个对象复制到新创建的
  • (普通的成员函数或析构函数) 虚函数的使用原则:可以把public或protected的部分成员函数声明为虚函数,一些特别函数不能申明为虚函数的原因: 1.普通函数(不能被覆盖)  2.友元函数(C++不支持友元函数...

空空如也

空空如也

1 2 3 4 5 6
收藏数 120
精华内容 48
关键字:

对象可以没有构造函数或析构函数