7个月之后的补充: 说真的,别再收藏这篇文章了,写的真的很拉跨,建议学习并收藏C++ 六个默认成员函数 + this指针_CPP的底层是哲学的博客-CSDN博客
也是我写的,质量会好很多!!!!!!
前言:
因为类对象的数据成员的值是很重要的 而对类对象数据成员初始化这一重要而频繁的操作 当然越简便越好 所以这里讲的就是对对象数据成员初始化的一些方法
9.1.1 对象的初始化
1. 在声明类时直接对数据成员进行初始化 如下所示
在某红书中说到 在声明类时对数据成员初始化是不对的 但是在 c++11标准下 是允许在声明类时 对数据成员直接赋初值的 因为这本书比较落后了 所以内容没有更新 在vs2019中 这种方法是没有报错的 但是注意考试的时候要填不能在声明类时直接对数据成员赋初值
2. 把数据成员声明为公用成员
把数据成员声明为公用成员后 在定义对象时 可以像定义结构体变量那样对数据成员初始化 但是绝大多数情况下 数据成员都不会声明为公用的 所以这种方法并不可取
9.1.2 用构造函数实现数据成员的初始化
c++提供了构造函数来处理对象的初始化
① 用户不能调用构造函数,在建立对象时自动执行,且只能执行一次。一般声明为public
② 构造函数是在声明类的时候由类的设计者定义的,程序用户只须在定义对象的同时指定数据成员的初值即可。
③ 构造函数名必须和类名同名,不能随意命名。
④ 构造函数不具有任何类型,不具有返回值。 可以有参数。
⑤ 可以用一个类对象初始化另一个类对象 如 Time t1; Time t2 = t1; 这样就把t1的各数据成员的值复制到t2相应的各成员 而不调用t2的构造函数Time();
⑥ 构造函数的函数体中还可以包含如cout的其他语句 但是不推荐这样做
⑦ 如果类的定义者没有自己定义构造函数,则c++系统会自动形成一个构造函数,这个构造函数函数体是空的,没有参数,不执行任何操作。
例
class Time
{
public:
Time()
{
hour = 0;
minute = 0;
sec = 0;
}
void set_time();
void show_time();
private:
int hour;
int minute;
int sec;
};
void Time::set_time()
{
cin >> hour >> minute >> sec;
}
void Time::show_time()
{
cout << hour << ":" << minute << ":" << sec << endl;
}
int main()
{
Time t1;
t1.set_time();
t1.show_time();
Time t2;
t2.show_time();
return 0;
}
这是构造函数的一个很简单的运用 这里的构造函数的功能是 给对象的数据成员赋初值为0 建立对象t1时调用构造函数 对象t1再调用了set_time函数 重新赋了一次值 而t2中的数据成员的值都是0
构造函数同样可以在类内声明 在类外定义 同样需要声明构造函数的所属类 以及作用于限定符 即
9.1.3 带参数的构造函数
引:上方给出的构造函数,是十分死板的。每个建立的对象都有同样的初始化值,无法达到不同对象不同初始化值的效果。所以有了带参数的构造函数。
即构造函数的参数列表中加若干参数 并在函数体中对不同数据成员赋值。 这样在建立对象并执行构造函数时,就可以把定义对象时给的一些数据赋给数据成员。
构造函数的实参只能在定义对象时给出,即建立对象的同时给出数据成员的初值。
// 带参数的构造函数
class Cube
{
public:
int volume();
Cube(int, int, int);
private:
int length;
int width;
int height;
};
Cube::Cube(int a, int b, int c)
{
length = a;
width = b;
height = c;
}
int Cube::volume()
{
return length * width * height;
}
int main()
{
//Cube c1 = { 10,20,30 };
Cube c1(10, 20, 30); //建立对象c1 并指定c1的长宽高 直接在后面加括号就行;
cout << "c1的体积为:" << c1.volume() << endl;
Cube c2(10, 10, 10);
cout << "c2的体积为:" << c2.volume() << endl;
return 0;
}
带参数的构造函数的声明以及类对象的定义方法如上所示,注意main函数中第一行第二行的方式都可以, 第二种可以看成给构造函数的形参赋实参。类似于函数调用。
9.1.4 用参数初始化表对数据成员初始化
不在构造函数函数体内执行初始化语句,放在函数首部实现。让构造函数显得简短精炼,就可以在类体内定义构造函数而不在类外定义。尤其当数据成员较多时更显其优越性,许多c++程序人员都喜欢用这种方式初始化所有数据成员。
// 参数初始化表对数据成员初始化
#include<string.h>
class cube
{
public:
cube(int l, int w, int h,string n) :length(l), width(w), height(h) ,name(n) //参数初始化表
{
//strcpy(arr, brr);
}
void show();
private:
int length;
int width;
int height;
//char arr[10];
string name;
};
void cube::show()
{
cout << name << ":" << length * width * height << endl;
}
int main()
{
cube c1(10, 20, 30, "yang");
c1.show();
cube c2(10, 10, 10, "li");
c2.show();
return 0;
}
格式如上,即: 在构造函数的函数首部后方再加一个冒号后面给出对应的值
注意:对于数组的初始化不可以像其他数据那样 因为数组不可以直接用等于号赋值(string可以)需要用strcpy这样的字符数组操作函数进行初始化。
9.1.5 构造函数的重载
即多个构造函数同名,但参数的个数不同,或参数的类型不同的构造函数。称为构造函数的重载
class Box
{
public:
int volume();
Box(int l, int w, int h) :length(l),width(w),height(h){} //有三个参数的构造函数
Box(int l,int w):length(l),width(w) //有两个参数的构造函数
{
height = 10;
}
Box(int l) :length(l) //有一个参数的构造函数
{
width = 10;
height = 10;
}
Box(); //无参构造函数
private:
int length;
int width;
int height;
};
int Box::volume()
{
return length * height * width;
}
Box::Box()
{
length = 10;
width = 10;
height = 10;
}
int main()
{
Box b1;
cout << "b1的体积为:" << b1.volume() << endl;
Box b2(20, 20, 20);
cout << "b2的体积为:" << b2.volume() << endl;
Box b3(20, 20);
cout << "b3的体积为:" << b3.volume() << endl;
Box b4(20);
cout << "b4的体积为:" << b4.volume() << endl;
return 0;
}
9.1.6 使用默认参数的构造函数
构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即如果用户不指定实参值,编译系统就使形参取默认值。
class cube
{
public:
cube(int l = 10, int w = 10, int h = 10):length(l),width(w),height(h){}
void show();
private:
int length;
int width;
int height;
};
void cube::show()
{
cout << "length=" << length <<" " << "width=" << width <<" " << "height=" << height << endl;
}
int main()
{
cube c1(20, 20, 20);
c1.show();
cube c2(20, 20);
c2.show();
cube c3(20);
c3.show();
cube c4;
c4.show();
return 0;
}
可以看到,在构造函数中使用默认参数是方便而有效的,它提供了建立对象时的多种选择,它的作用相当于好几个重载的构造函数。它的好处是:即使在调用构造函数时没有提供实参值,不仅不会出错,而且还确保按照默认的参数值对对象进行初始化。尤其在希望对每一个对象都有同样的初始化状况时用这种方法更为方便,不需输人数据,对象全按事先指定的值进行初始化。
注意:
(1)在建立对象时不必给出实参的构造函数,称为默认构造函数( defaultconstructor)。显然,无参构造函数属于默认构造函数。一个类只能有一个默认构造函数。如果用户未定义构造函数,则系统会自动提供一个默认构造函数,但它的函数体是空的,不起初始化作用。如果用户希望在创建对象时就能使数据成员有初值,就必须自己定义构造函数。
(2) 应该在什么地方指定构造函数的默认参数?应在声明构造函数时指定默认值,而不能只在定义构造函数时指定默认值。因为类声明是放在头文件中的,它是类的对外接口,用户是可以看到的,而函数的定义是类的实现细节,用户往往是看不到的。(类声明和类定义会放在两个文件中)。在声明构造函数时指定默认参数值,使用户知道在建立对象时怎样使用默认参数。
(3)程序第5行在声明构造函数时,形参名可以省略,即写成Box ( int = 10 , int = 10 , int = 10);
(4)如果构造函数的全部参数都指定了默认值,则在定义对象时可以给一个或几个实参,也可以不给出实参。由于不需要实参也可以调用构造函数,因此全部参数都指定了默认值的构造函数也属于默认构造函数。前面曾提到过:一个类只能有一个默认构造函数,也就是说,可以不用参数而调用的构造函数,一个类只能有一个。其道理是显然的,是为了避免调用时的歧义性。
(5)在一个类中定义了全部是默认参数的构造函数后,不能再定义重载构造函数。自行理解为什么
(6) 但如果构造函数中的参数并非全部为默认值时,(即部分指定默认值)就要分析具体情况。不是一定会出错,是很容易出错,要十分仔细。因此,一般不应同时使用构造函数的重载和有默认参数的构造函数。
总结:
(上方6点是我复制的= =) 构造函数,就是一个在对象建立时自动执行的函数,作用就是方便给对象的数据成员赋初值。 核心就是有参数的构造函数以及有默认参数的构造函数。 我认为这种是最方便的,在建立对象时可直接指定该对象的数据成员的值。
析构函数
析构函数是与构造函数作用相反的函数 当对象的生命期结束时,会自动执行析构函数。
析构函数不返回任何值,没有函数类型,也没有函数参数。由于没有函数参数,因此它不能被重载。一个类可以有多个构造函数,但是只能有一个析构函数。
实际上,析构函数的作用并不仅限于释放资源方面,它还可以被用来执行“类的设计者希望在最后一次使用对象之后所执行的任何操作”,例如输出有关的信息。
如果用户没有定义析构函数,C++编译系统会自动生成一个析构函数,但它只是徒有析构函数的名称和形式,实际上什么操作都不进行。想让析构函数完成任何工作,都必须在定义的析构函数中指定。
调用构造函数和析构函数的顺序
一般情况下: 先构造的后析构,后构造的先析构。
构造函数和析构函数在面向对象的程序设计中是相当重要的,是类的设计中的一个重要部分。