主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。即初始化
- 构造函数,没有返回值也不写void,同时函数体中不能有 return 语句。
- 函数名称与类名相同
- 和普通成员函数一样,构造函数是允许重载的。一个类可以有多个重载的构造函数,创建对象时根据传递的实参来判断调用哪一个构造函数。
- 一个类必须有构造函数,要么用户自己定义,要么编译器自动生成。一旦在类中定义了构造函数,那么创建对象时就一定要调用。如果用户自己没有定义构造函数,那么编译器会自动生成一个默认的构造函数,只是这个构造函数的函数体是空的,也没有形参,也不执行任何操作。
#include析构函数 主要作用在于对象销毁前系统自动调用,执行一些清理工作。using namespace std; class Student { private: char *m_name; int m_age; float m_score; public: //声明构造函数 Student( char *name, int age, float score); //声明普通成员函数 void show(); }; //定义构造函数 Student::Student( char *name, int age, float score){ m_name = name; m_age = age; m_score = score; } //定义普通成员函数 void Student::show(){ cout< show(); return 0; }
- 析构函数,没有返回值也不写void
- 函数名称与类名相同,在名称前加上符号 ~
- 析构函数不可以有参数(构造函数有参数),因此不可以发生重载,一个类只能有一个析构函数。如果用户没有定义,编译器会自动生成一个默认的析构函数。
- 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
#include二、构造函数的分类及调用 有三种构造函数(定义与声明): 1、无参构造(默认函数)using namespace std; class Person { public: //构造函数 Person() { cout << "Person的构造函数调用" << endl; }; //析构函数 ~Person() { cout << "Person的析构函数调用" << endl; }; }; void test01() { Person p; } int main() { test01(); return 0; }
#includeusing namespace std; class Person { public: //声明默认构造函数 Person(); int m_Age; int* m_Height; }; //定义默认构造函数 Person::Person() { cout << "默认构造函数的调用!" << endl; this->m_Age = 0; this->m_Height = new int(0); } int main() { Person p; cout << "此人的年龄是: " << p.m_Age << endl; cout << "此人的身高是: " << *(p.m_Height) << endl; return 0; }
2、有参构造
#includeusing namespace std; class Person { public: //声明默认构造函数 Person(); //声明有参构造 Person(int age, int height); int m_Age; int* m_Height; }; //定义默认构造函数 Person::Person() { cout << "默认构造函数的调用!" << endl; this->m_Age = 0; this->m_Height = new int(0); } //定义有参构造函数,把age赋值给m_Age,身高用m_Height指向 Person::Person(int age,int height) { cout << "有参构造函数的调用!" << endl; this->m_Age = age; this->m_Height = new int(height); } int main() { Person p(18,175); cout << "此人的年龄是: " << p.m_Age << endl; cout << "此人的身高是: " << *(p.m_Height) << endl; return 0; }
3.拷贝构造
上面都是开始是给出属性的值进行初始化,而拷贝构造函数在开始是传递一个对象,把对象的个属性拷贝到此对象中,进行对象的初始化(可以把传去的参数,看成一只羊,拷贝构造后克隆了一只新的羊,两只羊的属性一样)根据这样可以实现对象的创建。
#includeusing namespace std; class Person { public: //声明默认构造函数 Person(); //声明有参构造 Person(int age, int height); //声明拷贝构造 Person(const Person& p); int m_Age; int* m_Height; }; //定义默认构造函数 Person::Person() { cout << "默认构造函数的调用!" << endl; this->m_Age = 0; this->m_Height = new int(0); } //定义有参构造,把age赋值给m_Age,身高用m_Height指向 Person::Person(int age, int height) { cout << "有参构造函数的调用!" << endl; this->m_Age = age; this->m_Height = new int(height); } //定义拷贝构造函数调用 Person::Person(const Person& p) { cout << "拷贝构造函数的调用!" << endl; this->m_Age = p.m_Age; this->m_Height = new int(*p.m_Height); } int main() { Person p(18, 175); cout << "p的年龄是: " << p.m_Age << endl; cout << "p的身高是: " << *(p.m_Height) << endl; Person p2(p); cout << "p2的年龄是: " << p2.m_Age << endl; cout << "p2的身高是: " << *(p2.m_Height) << endl; return 0; }
三种调用方式(初始化)
- 括号法
- 显示法
- 隐式转换法
#include三、拷贝构造函数的调用using namespace std; class Person { public: //声明无参构造函数 Person(); //声明有参构造 Person(int age, int height); //声明拷贝构造 Person(const Person& p); int m_Age; int* m_Height; }; //定义无参构造函数 Person::Person() { cout << "默认构造函数的调用!" << endl; this->m_Age = 0; this->m_Height = new int(0); } //定义有参构造,把age赋值给m_Age,身高用m_Height指向 Person::Person(int age, int height) { cout << "有参构造函数的调用!" << endl; this->m_Age = age; this->m_Height = new int(height); } //定义拷贝构造函数调用 Person::Person(const Person& p) { cout << "拷贝构造函数的调用!" << endl; this->m_Age = p.m_Age; this->m_Height = new int(*p.m_Height); } int main() { //注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明 //Person p2(); Person p0; //1 括号法,常用 Person p(18, 175); cout << "p的年龄是: " << p.m_Age << endl; cout << "p的身高是: " << *(p.m_Height) << endl; Person p2(p); cout << "p2的年龄是: " << p2.m_Age << endl; cout << "p2的身高是: " << *(p2.m_Height) << endl; //2 显式法 Person p3 = Person(10,234); Person p4 = Person(p2); cout << "p3的年龄是: " << p3.m_Age << endl; cout << "p4的身高是: " << *(p4.m_Height) << endl; //2.3 隐式转换法 //Person p5 = 10,453; // Person p4 = Person(10); Person p6= p4; // Person p5 = Person(p4); cout << "p6的年龄是: " << p6.m_Age << endl; cout << "p6的身高是: " << *(p6.m_Height) << endl; //注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明 //Person p5(p4); return 0; }
拷贝构造函数的调用有以下三种情形:
- 用类的一个对象去初始化另一个对象时;
- 当函数的形参是类的对象时(值传递),如果是引用传递则不会调用;
- 当函数的返回值是类的对象或引用时。
#includeusing namespace std; class Point{ public: Point(int xx=0,int yy=0){x=xx;y=yy;} Point(const Point& p); int getX(){return x;} int getY(){return y;} private: int x,y; }; Point::Point(const Point& p){ x=p.x; y=p.y; cout<<"Calling the copy constructorn"< 四、构造函数的调用规则 默认情况下,c++编译器至少给一个类添加3个函数 构造函数调用规则如下:
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
- 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
无参构造函数
有参构造函数
拷贝构造函数
按照上面的顺序,定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造。定义拷贝构造函数,c++不会再提供其他构造函数
#include五、深拷贝与浅拷贝using namespace std; class Person { public: //无参(默认)构造函数 Person() { cout << "无参构造函数!" << endl; } //有参构造函数 Person(int a) { age = a; cout << "有参构造函数!" << endl; } //拷贝构造函数 Person(const Person& p) { age = p.age; cout << "拷贝构造函数!" << endl; } //析构函数 ~Person() { cout << "析构函数!" << endl; } public: int age; }; void test01() { Person p1(18); //如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作 Person p2(p1); cout << "p2的年龄为: " << p2.age << endl; } void test02() { //如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造 Person p1; //此时如果用户自己没有提供默认构造,会出错 Person p2(10); //用户提供的有参 Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供 //如果用户提供拷贝构造,编译器不会提供其他构造函数 Person p4; //此时如果用户自己没有提供默认构造,会出错 Person p5(10); //此时如果用户自己没有提供有参,会出错 Person p6(p5); //用户自己提供拷贝构造 } int main() { test01(); return 0; } C++中有两种拷贝,深拷贝与浅拷贝
- 在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的;但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象,所以,此时,必须采用深拷贝。
- 深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝。
c++的默认构造函数就是浅拷贝,浅拷贝就是简单的赋值操作,如果对象中没有其他的资源(如:堆,文件,系统资源等),则深拷贝和浅拷贝没有什么区别,如:
class A { public: A(int _data) : data(_data){} private: int data; }; int main() { A a(5), b = a; // 仅仅是数据成员之间的赋值 }这是浅拷贝,b=a调用了默认类中的拷贝构造函数,在这里使用深浅拷贝并没有任何区别。但是下面这种情况就不一定了:
class A { public: A(int _size) : size(_size) { data = new int[size]; } // 假如其中有一段动态分配的内存 ~A() { delete [] data; } // 析构时释放资源 private: int* data; int size; } int main() { A a(5), b = a; }这时,这里构造函数中有堆内存的分配,b=a也是调用的默认的拷贝构造函数,这时会出现一个什么样的情况呢?
a和b指向了同一块内存空间,所以当调用析构函数的时候,内存空间会被析构两次,所以这将导致内存泄露或程序崩溃。怎么解决呢?深拷贝–自定义拷贝构造函数
class A { public: A(int _size) : size(_size) { data = new int[size]; } // 假如其中有一段动态分配的内存 A(){}; A(const A& _A) : size(_A.size) //拷贝构造 { data = new int[size]; } // 深拷贝 ~A() { delete [] data; } // 析构时释放资源 private: int* data; int size; } int main() { A a(5), b = a; }这时我们的内存是什么样的情况呢,
c++深拷贝(两种)和浅拷贝_birate_小小人生的博客-CSDN博客_c++ 深拷贝
六、初始化列表 C++提供了初始化列表语法,用来初始化属性 语法:构造函数():属性1(值1),属性2(值2)... {}class Person { public: 传统方式初始化 //Person(int a, int b, int c) { // m_A = a; // m_B = b; // m_C = c; //} //初始化列表方式初始化 Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {} void PrintPerson() { cout << "mA:" << m_A << endl; cout << "mB:" << m_B << endl; cout << "mC:" << m_C << endl; } private: int m_A; int m_B; int m_C; }; int main() { Person p(1, 2, 3); p.PrintPerson(); return 0; }



