构造函数
- 类提供默认的无参构造函数和拷贝构造函数,如果自己手动添加构造函数,默认将不复存在,如果提供显示的构造,默认无参构造将会被隐藏,但是默认拷贝还在;如果显示的写了一个拷贝构造函数 ,会隐藏默认的无参构造函数和默认的拷贝构造函数
构造注意小细节
class Person
{
public:
Person()
{
cout << "无参构造" << endl;
}
Person(int a)
{
cout << "有参构造" << endl;
}
};
// 调用情况1.
Person p; //调用了无参构造
Person p(); //没有调用了无参构造,
// 调用情况2.
Person* p1 = new Person; // //调用了无参构造
Person* p2 = new Person(); //调用了无参构造
tips : 省事的话调用无参构造,直接不加括号
析构函数
- 类提供默认的析构函数,如果手动添加默认将不存在
- 析构顺序和构造顺序相反
应用场景
Test.h
class Test
{
public:
Test(int x, int y)
{
m_x = x;
m_y = y;
cout << "调用了有参数的构造函数" << endl;
}
//无参数的构造函数
Test(){
m_x = 0;
m_y = 0;
cout << "调用了无参数的构造函数" << endl;
}
//拷贝构造函数 ,想通过另一个Test对象 another 将本对象进行拷贝
Test(const Test & another)
{
m_x = another.m_x;
m_y = another.m_y;
cout << "调用了拷贝构造函数" << endl;
}
//等号操作符
void operator = (const Test &t)
{
cout << "调用了=号操作符" << endl;
m_x = t.m_x;
m_y = t.m_y;
}
void printT()
{
cout << "x : " << m_x << ", y : " << m_y << endl;
}
//提供一个析构函数
~Test()
{
cout << "~Test()析构函数被执行了" << endl;
cout << "(" << m_x << ", " << m_y << ")" << "被析构了" << endl;
}
private:
int m_x;
int m_y;
};
1.
void test1()
{
Test t1(1, 2); //调用t1的有参构造
Test t2(t1); //调用 t2的拷贝构造
//通过t1 给t2 进行赋值
t2.printT();
}
2.
void test2()
{
Test t1(1, 2);
Test t2;
t2 = t1; //调用的不是拷贝构造函数,调用的是=号操作符,也能够完成将t1的值给t2 但不是调用t2的拷贝构造函数。
}
3.
void func(Test t) //Test t(t1); //会调用局部变量t的拷贝构造函数
{
cout << "func begin..." << endl;
t.printT();
cout << "func end..." << endl;
}
void test3()
{
cout << "test3 begin ..." << endl;
Test t1(10, 20); //创建了一个t1的对象。通过t1的有参数的构造函数
func(t1);
cout << "test3 end..." << endl;
}
4.
Test func2()
{
cout << "func2 begin..." << endl;
Test temp(10, 20); //调用temp的带参数构造函数
cout << "func2 end.." << endl;
return temp; // 有一个临时的匿名对象 = temp ,把temp的数据给到了临时的匿名对象, 会调用这个临时匿名对象的拷贝构造函数, 将temp传进去。
}
void test4()
{
cout << "test4 begin " << endl;
func2();
//匿名对象在此被析构了, 如果一个临时的匿名对象,没有任何变量去接收它,编译器认为这个临时匿名对象没有用处。
//编译器会立刻销毁这个临时的匿名对象
cout << "test4 end" << endl;
}
5.
void test5()
{
cout << "test5 begin ..." << endl;
Test t1 = func2();//如果有一个变量去接收这个临时的匿名对象, 编译器认为这个匿名对象转正了,就不会立刻给他销毁。
//t1 = 匿名的临时对象 为什么不会发生拷贝构造
// 此时的t1 去接收这个匿名的临时对象不是 重新创建一个t1 而是给这个匿名对象起个名字就叫t1
//一旦这个匿名对象有了自己的名字,编译器就不会立刻给这个匿名对象销毁了,
//就当普通局部变量处理了
cout << "test5 end..." << endl;
//在此时析构的t1
}
6.
void test6()
{
cout << "test6 begin..." << endl;
Test t1; //调用t1的无参数构造函数
t1 = func2(); //调用的=号操作符 ,,t1 = 匿名对象。 调用了t1的=号操作符。
//此时匿名没有被转正,匿名没有自己的名字, 匿名对象这个内存没有自己的别名, 编译器就会立刻销毁。
cout << "test6 end..." << endl;
}
浅拷贝、深拷贝
- 有时候成员变量具有指针特性,一旦进行拷贝,指针指向同一块内存,当释放的时候,如果同一块内存被两次释放,程序将崩溃。这称之为浅拷贝。深拷贝的意思是将拷贝构造或者 =操作符重载的函数重写,重新开辟一块内存给需要赋值的指针,这样浅拷贝的问题将解决。
例子:
person.h
#pragma once
#include <iostream>
using namespace std;
class Person
{
public:
//有参数的构造函数
Person(int id, const char *name)
{
m_id = id;
int len = strlen(name);
m_name = (char*)malloc(len + 1);
strcpy_s(m_name, len + 1, name);
}
Person(const Person& another)
{
m_id = another.m_id;
int len = strlen(another.m_name);
m_name = (char*)malloc(len + 1);
strcpy_s(m_name, len+1, another.m_name);
}
~Person()
{
if (m_name != NULL)
{
free(m_name);
m_name = NULL;
}
}
private:
int m_id;
char *m_name;
};
user.c
#include "person.h"
using namespace std;
int main()
{
Person t1(1, "zhang3");
//如果不提供一个显示的拷贝构造函数, 通过系统自带的默认拷贝构造函数,程序会崩溃
Person t2(t1); //会调用t2的拷贝构造函数,将t1的值拷贝给t2
return 0;
}