某些函数在调用时不需要显式提供函数名,编译器会自动调用,例如类构造函数与类析构函数。
构造函数:
与类同名的成员函数。创建类的一个实例时,编译器自动调用合适的构造函数。
一个类至少有一个构造函数,即可以对构造函数进行重载;每个构造函数的函数签名不同。例:
#includeusing namespace std; class Person { public: //没有参数的默认构造函数 Person(); //参数类型为const string引用的带参构造函数 Person(const string& n); //参数类型为C风格字符串:const char*的带参构造函数 Person(const char* n); void setName(const string& n); void setName(const char* n); const string& getName()const; private: string name; };
构造函数的使用:
int main() {
//默认构造函数
Person per;
//带参构造函数
Person IvS("Ivan Sutherland");
}
顾名思义,构造函数用于"构造",对数据成员进行初始化,并且负责其他一些对象创建时需要的事务。
构造函数最大的特点:
1.函数名与类名相同
2.没有返回类型
构造函数与其它函数相同,可以进行赋值、判断、循环、调用等功能;可以在类内/类外定义;
对象数组与默认构造函数:
假设C是一个类,可以定义任意维数的C对象数组;若C拥有默认构造函数,数组中每个C对象都会调用默认构造函数。例如:
#pragma once #includeusing namespace std; unsigned n = 0; class C { public: C() { cout << "C" << ++n << "n"; } }; C arr[10];
测试:
#include#include"ctest.h" using namespace std; int main(){ C(); };
输出结果:
构造函数约束对象的创建:
程序员将部分构造函数设计为私有,另一部分设计为公有;私有构造函数不能在类外调用,由类范围属性。
关于构造函数的自动生成:
编译器找不到任何构造函数时,会生成一个公有默认构造函数;
若一个类已声明构造函数,则不会生成公有默认构造函数;
一个类声明了一个非公有的默认构造函数,编译器不生成公有默认构造函数。
在带参构造函数中有两类比较重要:
拷贝构造函数:创建一个新的对象,此对象是另外一个对象的拷贝品;
转型构造函数:用于类型间的转换,只有一个参数,但是第一个之后的每个参数都必须有默认值;
拷贝构造函数:
拷贝构造函数的原型(必须是引用):
//正确: Person(const Person&); Person(Person&); //以下错误: //Person (Person); //会提示:"'类Person'的复制构造函数不能带有'Pesron'类型的参数"
//多参数: Person( const Person &p,bool married = false);
若类设计者不提供拷贝构造函数,编译器将自动生成。完成的操作:
将源对象所有数据成员的值逐一赋值给目标对象相应的数据成员
一个类包含指向动态存储空间指针类型的数据成员,就为这个类设计拷贝构造函数
例:
#pragma once #includeusing namespace std; class nameList { public: nameList() { size = 0; p = 0; } nameList(const string[], int); void set(const string&, int); void set(const char*, int); private: int size; string* p; }; nameList::nameList(const string s[], int si) { size = si; p = new string[size]; for (int i = 0; i < size; i++) { p[i] = s[i]; } }
#include#include"ctest.h" using namespace std; int main() { string list[] = { "A","B","C" }; nameList d1(list, 3); d1.dump(); //A,B,C nameList d2(d1); d2.dump(); //A,B,C d2.set("D", 1); d2.dump(); //A,D,C d1.dump(); //A,D,C return 0; }
上例没有为类nameList定义拷贝构造函数,则定义d2时将会调用编译器提供的拷贝构造函数,将d1的数据成员拷贝到d2;此时指针d1.p和d2.p指向同一块储存空间:数组的第一项的地址。
可能引发危险:操作一个可能会改变另一个的内容。
为避免错误,为nameList类提供一个满足要求的拷贝构造函数。
禁止对象拷贝:有些对象较大,进行对象的拷贝操作(例如采用传值方式将对象传递给一个函数或者返回一个对象时),浪费资源。此时通过将拷贝构造函数设计成私有成员来禁止对象间的拷贝操作。
关于对象拷贝:
转型构造函数:
一个单参数的构造函数;将一个对象从一种数据类型(由参数指出)转换为另一种数据类型(该构造函数所属的类)
假设函数f的参数类型为Person对象,以一个string作为参数来调用f;
//f的参数类型为Person对象 void f(Person p); //以一个string类型作为参数来调用f string s = "STRING"; f(s);
需要Person类拥有一个将string转型成Person的转型构造函数,编译器就可以在string对象s上调用这个转型构造函数,构造一个Person对象做f的参数。
转型构造函数与隐式类型转换:
例中的Person类的转型构造函数支持隐式类型转换。
隐式类型转换方便但是可能有细微的问题;可以用关键字explicit关闭系统的隐式类型转换功能。
#includeusing namespace std; class Person { public: //标记为explicit explicit Person(const string& n) { name = n; } //函数f的形参类型:Person void f(Person s); private: string name; }; int main() { Person p("per1"); f(p); //OK; p is a Person //使用转型构造函数 string b = "per2"; f(b); //ERROR; b is a string //explicit关键字关闭隐式类型转换 }
构造函数的初始化:
对const类型的数据成员不能直接赋值,需要为构造函数添加一个初始化列表
class C {
public:
C() :c(0){x = -1;}
private:
const int c;
int x;
};
构造函数初始化程序:
初始化段:冒号+要进行初始化的数据成员+初始值
C() :c(0){x = -1;}
初始化列表的有效范围:
构造函数中,构造函数可以初始化const或者非const数据成员;const数据成员只能用初始化列表初始化。
class C {
public:
C() :c(0),x(-1){}
private:
const int c;
int x;
};
构造函数与操作符new和new[]:
使用动态方式为一个对象分配存储空间,C++操作符new和new[]飞陪存储空间并且调用相应的构造函数。
析构函数:
类比于对象的创建,在摧毁对象时,会自动调用一个析构函数。(对象的摧毁:以某个类作为数据类型的变量超出其作用范围;用delete操作符删除动态分配的对象)
特点:
1.是一个成员函数
2.(类C) 析构函数的原型:~C();
3.析构函数不带参数,不能被重载,所以每个类只有一个析构函数。
4.析构函数没有返回类型
创建对象时类构造函数完成初始化和其他相关操作;摧毁对象时析构函数完成清理工作(例如将构造函数分配的资源释放).
————————————2021-11-05-19:17————————————



