【C/C++学院】(6)构造函数/析构函数/拷贝构造函数/深copy浅copy

1.构造函数

类的初始化即为构造函数。也为:隐式的初始化。

构造函数在对象初始化的时候,自动被调用。隐式的调用。

构造函数分为三种:有参构造函数、无参构造函数、拷贝构造函数。

有参构造函数调用有三种:括号法、等号法、手工法。

#include <iostream>
using namespace std;

class Test
{
private:
	int m_a;
public:
	Test()//无参构造函数
	{	}
	Test(const Test &obj)//拷贝构造函数
	{	}
	Test(int a)//有参构造函数
	{
		m_a = a;
	}
	void print()
	{
		cout << "m_a:" << m_a << endl;
	}

};

void main()
{
	Test t1(10);//括号法  //c++编译器自动调用这个类的有参构造函数
	t1.print();
	Test t2 = 20;//等号法  //c++编译器自动调用这个类的有参构造函数
	t2.print();
	Test t3 = Test(30);//手工法 //程序员手工的调用构造函数 进行对象初始化
	t3.print();
	system("pause");
}

2.析构函数

析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后”
的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。

主函数结束的同时,对象stud1,stud2均应被“清理”,而清理就是通过调用了析构函数实现的。

#define _CRT_SECURE_NO_WARNINGS
#include<string>
#include<iostream>
using namespace std;

class stud//声明一个类
{
private://私有部分
	int num;
	char name[10];
	char sex;
public://公用部分
	stud(int n, char nam[], char s)//构造函数
	{
		num = n;
		strcpy(name, nam);
		sex = s;
	}
	~stud()//析构函数
	{
		cout << "stud has been destructed!" << endl;//通过输出提示告诉我们析构函数确实被调用了
	}
	void display()//成员函数
	{
		cout << "num:" << num << endl;
		cout << "name:" << name << endl;
		cout << "sex:" << sex << endl;
	}
};
int main()
{
	stud stud1(10010, "Wang-li", 'f');
	stud stud2(10011, "Zhang-fun", 'm');//建立两个对象
	stud1.display();//输出学生1的数据
	stud2.display();//输出学生2的数据
	system("pause");
	return 0;
}

3.拷贝构造函数

拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。

当我们没有编写拷贝构造函数的时候,c++编译器会默认给我们提供一个拷贝构造函数,执行的是浅拷贝。

copy构造函数四种应用场景;

第一种场景:=

#include <iostream>
using namespace std;
class CExample {
private:
    int a;
public:
    //构造函数
    CExample(int b)
    {
        a = b;
    }
    //拷贝构造函数
    CExample(const CExample& C)
    {
        a = C.a;
    }
    //一般函数
    void Show()
    {
        cout << a << endl;
    }
};
int main()
{
    CExample A(100);
    CExample B = A; //注意这里的对象初始化要调用拷贝构造函数,而非赋值
    // CExample B(A); 也是一样的
    B.Show();
    return 0;
}  

第二种场景:()

#include <iostream>
using namespace std;
class CExample {
private:
    int a;
public:
    //构造函数
    CExample(int b)
    {
        a = b;
    }
    //拷贝构造函数
    CExample(const CExample& C)
    {
        a = C.a;
    }
    //一般函数
    void Show()
    {
        cout << a << endl;
    }
};
int main()
{
    CExample A(100);
    //CExample B = A; //注意这里的对象初始化要调用拷贝构造函数,而非赋值
    CExample B(A); //也是一样的
    B.Show();
    return 0;
}  

第三种场景:对象以值传递的方式传入函数参数

#include <iostream>
using namespace std;
class CExample
{
private:
    int a;
public:
    //构造函数
    CExample(int b)
    {
        a = b;
        cout << "creat: " << a << endl;
    }
    //拷贝构造
    CExample(const CExample& C)
    {
        a = C.a;
        cout << "copy" << endl;
    }
    //析构函数
    ~CExample()
    {
        cout << "delete: " << a << endl;
    }
    void Show()
    {
        cout << a << endl;
    }
};
//全局函数,传入的是对象
void g_Fun(CExample C)
{
    cout << "test" << endl;
}
int main()
{
    CExample test(1);
    //传入对象
    g_Fun(test);
    return 0;
} 

第四种场景:对象以值传递的方式从函数返回

#include <iostream>
using namespace std;
class CExample
{
private:
    int a;
public:
    //构造函数
    CExample(int b=0)
    {
        a = b;
        cout << "a:" << a << endl;
    }
    ~CExample()
    {
        cout << "destroy a:" << a << endl;
    }
    //拷贝构造
    CExample(const CExample& C)
    {
        a = C.a;
        cout << "copy a:"<< a << endl;
    }
};
//全局函数
CExample g_Fun()
{
    CExample temp(10);
    return temp;
}
int main()
{
    CExample ret;
    ret = g_Fun();
    return 0;
}  

添加断点,逐条语句运行,观察程序的执行步骤以及打印输出:

4.深copy浅copy

在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

  深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。

#include "iostream"

using namespace std;

class name

{ 

public :

	name(char *pn) ;  

	name( name &obj)

	{

		cout <<" copy Constructing " << endl ;

		char *pn = obj.getPn();

		pname = (char *)malloc(strlen(pn) +1);

		if (pname!=NULL) strcpy(pname,pn) ;

		//pname = new char[strlen(pn)+1] ;

		//if (pname!=0) strcpy(pname,pn) ;

		size = strlen(pn) ;

	}

	~ name() ;

protected : 

	char *pname ;       int size ;

public:

	char * getPn()

	{

		return pname;

	}	

	void operator=(name &obj1)

	{

		cout <<" 执行=操作" << endl ;

		char *pn = obj1.getPn();

		pname = (char *)malloc(strlen(pn) +1);//此处malloc了内存,没有free,存在一个潜在的bug

		if (pname!=NULL) strcpy(pname,pn) ;

		//pname = new char[strlen(pn)+1] ;

		//if (pname!=0) strcpy(pname,pn) ;

		pname[0] = 'm';

		size = strlen(pn) ;

	}

} ;

name::name(char *pn)

{ 

	cout <<" Constructing " << pn << endl ;

	pname = (char *)malloc(strlen(pn) +1);

	if (pname!=0) strcpy(pname,pn) ;

	//pname = new char[strlen(pn)+1] ;

	//if (pname!=0) strcpy(pname,pn) ;

	size = strlen(pn) ;

} 

name :: ~ name()

{ 

	cout << " Destructing " << pname << endl ; 

	pname[0] = '\0' ;

	//delete  []pname ;

	free(pname);

	size = 0 ;

}
void playmain()
{
	name obj1("name1");
	//如果你不写copy构造函数,那么C++编译器会给我们提供一个默认的copy构造函数 (浅cpy)
	name obj2 = obj1;
	//如果你不写=操作,那么C++编译器会给我们提供一个=操作函数 (浅cpy)
	obj2 = obj1;
	cout<<obj2.getPn()<<endl;
}
void main()
{
	playmain();
	system("pause");
}

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std;
class name
{
public:
    name(char *pn);
    name(name &obj)
    {
        cout << " copy Constructing " << endl;
        char *pn = obj.getPn();
        pname = (char *)malloc(strlen(pn) + 1);
        if (pname != NULL) strcpy(pname, pn);
        //pname = new char[strlen(pn)+1] ;
        //if (pname!=0) strcpy(pname,pn) ;
        size = strlen(pn);
    }
    ~name();
protected:
    char *pname;       int size;
public:
    char * getPn()
    {
        return pname;
    }
    void operator=(name &obj1)
    {
        cout << " 执行=操作" << endl;
        if (pname != NULL)//此处有一个疑问:如果不free, 直接将原来的内存进行重新赋值
        {
            char *pn = obj1.getPn();
            strcpy(pname, pn);
            pname[0] = 'N';
            size = strlen(pn);
        }
    }
    /*
    void operator=(name &obj1)
    {
        cout <<" 执行=操作" << endl ;
        if (pname != NULL)//此处有一个疑问:如果不free, 直接将原来的内存进行重新赋值
        {
            free(pname);
            pname = NULL;
            size = 0;
        }
        char *pn = obj1.getPn();
        pname = (char *)malloc(strlen(pn) +1);
        if (pname!=NULL) strcpy(pname,pn) ;
        //pname = new char[strlen(pn)+1] ;
        //if (pname!=0) strcpy(pname,pn) ;
        pname[0] = 'm';
        size = strlen(pn) ;
    }
    */
};
name::name(char *pn)
{
    cout << " Constructing " << pn << endl;
    pname = (char *)malloc(strlen(pn) + 1);
    if (pname != 0) strcpy(pname, pn);
    //pname = new char[strlen(pn)+1] ;
    //if (pname!=0) strcpy(pname,pn) ;
    size = strlen(pn);
}
name :: ~name()
{
    cout << " Destructing " << pname << endl;
    pname[0] = '\0';
    //delete  []pname ;
    free(pname);
    size = 0;
}
int playmain()
{
    name obj1("name1");
    name obj3("name3");
    //如果你不写copy构造函数,那么C++编译器会给我们提供一个默认的copy构造函数 (浅cpy)
    name obj2 = obj1;
    //做业务逻辑
    //此处省略500行
    //如果你不写=操作,那么C++编译器会给我们提供一个=操作函数 (浅cpy)
    //会调用对象2 的=号操作函数, obj3是形参, obj2干什么去了?
    obj2 = obj3;
    cout << obj2.getPn() << endl;
    return 0;
}
int main()
{
    playmain();
    //system("pause");
    return 0;
}

最终分析图:

时间: 2024-10-15 22:47:22

【C/C++学院】(6)构造函数/析构函数/拷贝构造函数/深copy浅copy的相关文章

c++类大四个默认函数-构造函数 析构函数 拷贝构造函数 赋值构造函数

每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数).对于任意一个类A,如果不编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如: A(void);//缺省的无参数构造函数 A(const A&a);//缺省的拷贝构造函数 -A();//缺省的析构函数 A&operator=(const A &a);//缺省的赋值构造函数 1).“缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘

c++ 构造函数,拷贝构造函数,析构函数与赋值操作符

题目: 为下面的Rectangle类实现构造函数,拷贝构造函数,赋值操作符,析构函数. class Shape { int no; }; class Point { int x; int y; }; class Rectangle: public Shape { int width; int height; Point * leftUp; public: Rectangle(int width, int height, int x, int y); Rectangle(const Rectang

C++中构造函数,拷贝构造函数,析构函数

C++中默认构造函数就是没有形参的构造函数.准确的说法,按照<C++ Primer>中定义:只要定义一个对象时没有提供初始化式,就是用默认构造函数.为所有 的形参提供默认实参的构造函数也定义了默认构造函数. 合成的默认构造函数,即编译器自动生成的默认构造函数.<C++ Primer>中的说明:一个类哪怕只定义了一个构造函数,编译器也不会再生成默认构造函数.这条规则的根据是,如果一个类再某种情况下需要控制对象初始化,则该类很可能在所有情况下都需要控制.只有当一个类没有定义构造函数时,

构造函数、拷贝构造函数和析构函数的的调用时刻及调用顺序

构造函数.拷贝构造函数和析构函数的的调用时刻及调用顺序 对象是由“底层向上”开始构造的,当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的构造函数,依次类推,直至到达派生类次数最多的派生次数最多的类的构造函数为止.因为,构造函数一开始构造时,总是要调用它的基类的构造函数,然后才开始执行其构造函数体,调用直接基类构造函数时,如果无专门说明0,就调用直接基类的默认构造函数.在对象析构时,其顺序正好相反.   下面简单介绍下这三个函数. 构造函数       1.构造函数不能有返回值  

【编程题】编写String类的构造函数、拷贝构造函数、析构函数和赋值函数

[编程题]编写String类的构造函数.拷贝构造函数.析构函数和赋值函数 [题目]:请编写如下4个函数 1 class String 2 { 3 public: 4 String(const char *str = NULL);// 普通构造函数 5 String(const String &other); // 拷贝构造函数 6 ~ String(void); // 析构函数 7 String & operate =(const String &other);// 赋值函数 8

构造函数、拷贝构造函数、析构函数

构造函数:在对象被创建时利用特定的初始值构造对象,即初始化对象. 拷贝构造函数:用一个已经存在的对象去初始化另一个对象,这两个对象的类型应该是一样的. 格式: class 类名 { public: 类名(形参);                 //构造函数 类名(类名 &对象名);    //拷贝构造函数 ... }; 类名::类名(形参)            //构造函数的实现 { 函数体 } 类名::类(类名 &对象名)      //拷贝构造函数的实现 { 函数体 } 构造函数在

CPP_类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数

类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数 class Person{ public : Person():age(10), name(NULL); Person(int myage, char *myname); Person(const Person &a); ~Person(void); void set_age(int myage); void get_age(void); void set_other_age(Person &a, int b); private: i

C++构造函数和拷贝构造函数详解

构造函数.析构函数与赋值函数是每个类最基本的函数.它们太普通以致让人容易麻痹大意,其实这些貌似简单的函数就象没有顶盖的下水道那样危险. 每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数). 对于任意一个类A,如果不想编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如: A(void); // 缺省的无参数构造函数 A(const A &a); // 缺省的拷贝构造函数 ~A(void); // 缺省的析构函数 A & op

C++空类编译器自动生成的6个成员函数、关于构造函数、拷贝构造函数的解释

对于空类,编译器不会生成任何的成员函数,只会生成1个字节的占位符. 有时可能会以为编译器会为空类生成默认构造函数等,事实上是不会的,编译器只会在需要的时候生成6个成员函数:默认构造函数.默认拷贝构造函数.默认析构函数.默认赋值运算符 这四个是我们通常大都知道的.但是除了这四个,还有两个,那就是取址运算符和 取址运算符 const. class Empty { public: Empty(); //缺省构造函数 Empty(const Empty &rhs); //拷贝构造函数 ~Empty();