C++的构造函数

时间:2022-07-08 01:30:19

C++的构造函数

tags: c++

构造函数

构造函数是用于创建对象的特殊成员函数, 当创建对象时,系统自动调用构造函数构造函数的作用是:
* 为对象分配空间;对数据成员赋初值;请求其他资源
* 没有用户定义的构造函数时,系统提供缺省版本的构造函数

构造函数名与类名相同:类名
构造函数可以重载,构造函数可以有任意类型的参数,但没有返回类型。

析构函数

析构函数是用于取消对象的成员函数,当一个对象作用域结束时,系统自动调用析构函数。析构函数的作用是进行清除对象,释放内存等。没有用户定义析构函数时,系统提供缺省版本的析构函数。
析构函数名为:~ 类名
析构函数没有参数,也没有返回类型。

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

拷贝构造函数用一个已有同类对象的数据对正在建立的对象进行数据初始化,C++为类提供默认版本的复制构造函数,程序员可以定义用户版本的复制构造函数。
语法形式
类名 :: 类名(const 类名 & 引用名 , …);

调用拷贝构造函数的四个场景

  • 我们显式地调用拷贝构造函数,直接用对象去初始化一个新的对象
#include "iostream"
using namespace std;

class Test
{
public:
//有参构造函数
Test(int a)
{
m_a = a;
}
//无参数构造函数
Test()
{
m_a = 0;
}
//赋值构造函数 copy构造函数
Test(const Test &obj)
{
cout << "我也是构造函数啊。。。。。。" << endl;
}
private:
int m_a;
};

void main()
{
Test t1;
Test t2(t1); //用t1来初始化t2,调用拷贝构造函数
}
  • 第二种方法如下,当我们把类的对象当做形参,用已有的类对象做实参调用该函数时:
#include <iostream>
using namespace std;

class Location
{
public:
Location( int xx = 0 , int yy = 0 )
{
X = xx ;
Y = yy ;
cout << "Constructor Object.\n";
}

Location( const Location & p ) //复制构造函数
{
X = p.X ;
Y = p.Y ;
cout << "Copy_constructor called." << endl ;
}

~Location()
{
cout << X << "," << Y << " Object destroyed." << endl;
}
int GetX ()
{
return X;
}
int GetY ()
{
return Y;
}

private :
int X , Y;
} ;
void f (Location p)
{
cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl;
}
void playobjmain()
{
Location A ( 1, 2 ) ;
f(A);//将对象A传递给f函数做实参,会调用拷贝构造函数,很好理解,这和前面的第一种方法类似,用对象A初始化一个新构建的Location对象
}

int main()
{
playobjmain();
//system("pause");
return 0;
}
  • 第三种场景比较复杂,我们特别要区分好拷贝构造函数和“=”。
Test t1;
Test t2 = t1;
/**
上面是用t1初始化t2,调用的是拷贝构造函数。
*/

Test t3;
t3 = t1;
/**
t3已经初始化过了,因此不会再调用拷贝构造函数,它会调用“=”操作。
*/
#include <iostream>
using namespace std;

class Test
{
public:

//有参构造函数
Test(int a)
{
m_a = a;
}
//无参数构造函数
Test()
{
m_a = 0;
}
//赋值构造函数 copy构造函数
Test(const Test &obj)
{
cout << "我也是构造函数啊。。。。。。" << endl;
}
public:
void print()
{
cout << "m_a" << m_a << endl;
}
private:
int m_a;
};

void play()
{

Test t1;
Test t3;
/**
1. 赋值构造函数和=操作是两个不同的概念
2. 赋值构造函数(copy构造函数)也是构造函数
*/

//第三种场景,t2被创建,并且自动调用copy构造函数
Test t2 = t1; //对象t2的初始化,当我们没有编写copy构造函数,执行的是浅拷贝
t2 = t1; //是对象的“=”(赋值)操作
}

void main()
{
play();
Test t1;
//直接用t1初始化t2,调用拷贝构造函数
Test t2(t1);
system("pause");
}
/**
运行结果如下:
我也是构造函数啊。。。。。。
我也是构造函数啊。。。。。。
*/

  • 第四种场景比较复杂,具体是函数将类的对象当做返回值传出的时候。
#include "iostream"
using namespace std;

class Location
{
public:
Location(int xx = 0, int yy = 0)
{
X = xx;
Y = yy;
cout << "Constructor Object.\n";
}
Location(const Location & p) //复制构造函数
{
X = p.X;
Y = p.Y;
cout << "Copy_constructor called." << endl;
}
~Location()
{
cout << X << "," << Y << " Object destroyed." << endl;
}
int GetX()
{
return X;
}
int GetY()
{
return Y;
}
private:
int X, Y;
};

void f(Location p)
{
cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl;
}


Location g()
{
Location A(1, 2);
return A;
/**
将A传回去的时候,会创建一个匿名对象,因为主函数调用g函数的时候,g中的变量都在栈里面,函数结束之后东西会被清空掉,因此,返回的时候会c++会构建一个匿名的对象,并用A来初始化该匿名对象,然后才会调用A的析构函数销毁A
*/

}
void play()
{
Location B;
B = g();
/**
调用“=”运算符,用返回的匿名对象给B赋完值之后,匿名对象会马上析构掉,你可以运行改程序查看一下。
*/

printf("hello, boy!\n");
}

int main()
{
play();
system("pause");
return 0;
}

/**
Constructor Object.
Constructor Object.
Copy_constructor called.
1,2 Object destroyed.
1,2 Object destroyed.
hello, boy!
1,2 Object destroyed.
*/

深拷贝和浅拷贝

即使我们一个类里面什么东西也不写,c++也会默认给我们提供一些东西:

  • 无参构造函数
  • 拷贝构造函数
  • 析构函数
  • 赋值操作函数

当然默认提供的无参构造函数和析构函数几乎什么也不干。拷贝构造函数和赋值操作函数就是单纯的内存拷贝,也就是浅拷贝。

#include <iostream>
using namespace std;

class name
{
public:
name(char *pn);
~name();
protected:
char *pname;
int size;
public:
char * getPn()
{
return pname;
}
};
name::name(char *pn)
{
cout << " Constructing " << pn << endl;
pname = (char *)malloc(strlen(pn) + 1);
if (pname != 0) strcpy(pname, pn);
size = strlen(pn);
}
name :: ~name()
{
cout << " Destructing " << pname << endl;
pname[0] = '\0';
free(pname);
size = 0;
}


void playmain()
{
name obj1("name1");
//如果你不写copy构造函数,那么C++编译器会给我们提供一个默认的copy构造函数 (浅copy)
name obj2 = obj1;
}
/**
程序会扑街,c++默认提供的拷贝构造函数等同于memcpy,obj2中的pname和obj1中的pname指向同一处地方,playmain析构的时候pname指向的地址会被free两次,自然会出错。
*/

void main()
{
playmain();

system("pause");
}

如果改为赋值操作,也会出现相同的问题:

#include <iostream>
using namespace std;

class name
{
public:
name(char *pn);
name();
~name();
protected:
char *pname; int size;
public:
char * getPn()
{
return pname;
}
};
name::name()
{
cout << " Constructing " << endl;
pname = NULL;
size = 0;
}
name::name(char *pn)
{
cout << " Constructing " << pn << endl;
pname = (char *)malloc(strlen(pn) + 1);
if (pname != 0) strcpy(pname, pn);
size = strlen(pn);
}
name :: ~name()
{
cout << " Destructing " << pname << endl;
pname[0] = '\0';
free(pname);
size = 0;
}


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

void main()
{
playmain();
system("pause");
}

原因是类似的,对于这中情况,我们要拒绝编译器给我们默认生成的“=”和拷贝构造函数:

#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);
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);
if (pname != NULL) strcpy(pname, pn);

size = strlen(pn);
}
};
name::name(char *pn)
{
cout << " Constructing " << pn << endl;
pname = (char *)malloc(strlen(pn) + 1);
if (pname != 0) strcpy(pname, pn);
size = strlen(pn);
}
name :: ~name()
{
cout << " Destructing " << pname << endl;
pname[0] = '\0';
free(pname);
size = 0;
}


void playmain()
{
name obj1("name1");
//如果你不写copy构造函数,那么C++编译器会给我们提供一个默认的copy构造函数 (浅copy)
name obj2 = obj1;

//如果你不写=操作,那么C++编译器会给我们提供一个=操作函数 (浅copy)
obj2 = obj1;
cout << obj2.getPn() << endl;
}

void main()
{
playmain();
system("pause");
}