文章目录
【 1. 构造函数 】
- 类的构造函数是类的一种特殊的成员函数,它会 在每次创建类的新对象时执行。
- 构造函数的名称与类的名称是完全相同的,并且 不会返回任何类型,也不会返回 void。构造函数可 用于为某些成员变量设置初始值。
1.1 带参构造函数–传入数据
- 法1
class Complex
{
private:
double x;
double y;
public:
Complex(); //无参构造函数
Complex(double, double); //带参构造函数
void Complex_Printf(void); //输出函数
};
//在类的外部定义构造函数
Complex::Complex(double a, double b)
{
x = a;
y = b;
}
class Complex
{
private:
double x;
double y;
public:
Complex(); //无参构造函数
Complex(double a, double b) //在类的内部定义构造函数
{
x = a;
y = b;
}
void Complex_Printf(void); //输出函数
};
- 法2
//带参构造函数--形式2-赋默认值
Complex::Complex(double a=3.1, double b=6.2)
{
x = a;
y = b;
}
- 法3
//带参构造函数--法3
Complex::Complex(double a,double b) :x(a), y(b) { };
- 法4
//带参构造函数--法4-赋默认值
Complex::Complex(double a=3.1,double b=6.2) :x(a), y(b) { };
1.2 无参构造函数–不传入数据
- 法1
class Complex
{
private:
double x;
double y;
public:
Complex(); //无参构造函数
Complex(double, double); //带参构造函数
void Complex_Printf(void); //输出函数
};
//无参构造函数:默认值x=2,y=1
Complex::Complex()
{
x=2;
y=1;
};
- 法2
//无参构造函数:默认值x=2,y=1
Complex::Complex() :x(2), y(1) { };
1.3 实例
1.3.1 实例1-单个文件类的定义方法
#include <iostream>
using namespace std;
class Box
{
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
public:
// 带参构造函数-法1
Box(double l, double b, double h)
{
length = l;
breadth = b;
height = h;
}
// 带参构造函数-法2-赋默认值
/*Box(double l=2.0, double b=3.0, double h=4.0)
{
length = l;
breadth = b;
height = h;
}*/
// 带参构造函数-法3
//Box(double l, double b, double h) :length(l), breadth(b), height(h) { };
//带参构造函数-法4-赋默认值
//Box(double l=2.0, double b=3.0, double h=4.0) :length(l), breadth(b),height(h) { };
无参构造函数-法1
//Box::Box()
//{
// x=2;
// y=1;
//};
无参构造函数-法2
//Box() :length(10), breadth(2), height(5) { };
double Volume()
{
return length * breadth * height;
}
};
int main(void)
{
Box Box1(1.0, 2.2, 3.0);
/* 适用于:
带参构造函数-法1
带参构造函数-法2-赋给定值
带参构造函数法3
带参构造函数-法4-赋给定值
*/
// Box Box1;
/* 适用于:
带参构造函数-法2-赋默认值
带参构造函数-法4-赋默认值
无参构造函数法1
无参构造函数法2
*/
cout << Box1.Volume() << endl;
return 0;
}
1.3.2 实例2-多个文件类的定义方法
// 【Complex.h】
#pragma once
class Complex
{
private:
double x;
double y;
public:
Complex(); //无参构造函数
Complex(double, double); //带参构造函数
void Complex_Printf(void); //输出函数
};
// 【 Complex.cpp 】
#include "Complex.h"
#include <iostream>
using namespace std;
//无参构造函数:默认值x=2,y=1
Complex::Complex() :x(2), y(1) {};
//带参构造函数:赋初值
Complex::Complex(double a, double b)
{
x = a;
y = b;
}
//输出函数,根据实部x和虚部y,输出对应的复数
void Complex:: Complex_Printf(void)
{
if (!x && !y) cout << '0' << endl; //{0} {0} :0
else if (x && !y) cout << x << endl; //{≠0}{0} :x
else if (!x && y == -1) cout << '-i' << endl; //{0} {-1} :-i
else if (!x && y == 1) cout << 'i' << endl; //{0} {1} :i
else if (!x) cout<<y<<'i'<<endl; //{0} {else}:yi
else if (x && y == 1) cout << x << "+i" << endl; //{≠0}{1} :x+i
else if (x && y == -1) cout << x << "-i" << endl; //{≠0}{-1} :x-i
else if (y > 0) cout << x << '+' << y << 'i'<<endl;//{≠0}{>0} :x+yi ,y>0
else cout << x <<y << 'i' << endl; //{≠0}{<0} :x-yi ,y<0
}
// 【 Main.cpp 】
#include <iostream>
#include "Complex.h"
using namespace std;
int main(void)
{
Complex a;
Complex b(0, 0);
Complex c(0.2, 3.7);
Complex d(0, 1);
Complex e(2, 0);
Complex f(3, -5);
a.Complex_Printf();
b.Complex_Printf();
c.Complex_Printf();
d.Complex_Printf();
e.Complex_Printf();
f.Complex_Printf();
return 0;
}
1.4 数组类的构造-在构造函数内输入数组
- 实例
第一行输入一个整数n,表示数组的大小,第二行输入n个整数,表示数组,最后输出所有数组元素。
#include <iostream>
using namespace std;
class Array {
private:
int n;//数组大小
int* a;//数组
public:
// 构造函数
Array()
{
cin >> n;
a = new int[n];
for (int j = 0; j < n; ++j)
cin >> a[j];
}
// 析构函数
~Array() {
delete[]a;
}
// 输出数组
void show() {
for (int i = 0; i < n; i++) cout << a[i] << ' ';
}
};
int main() {
Array a;
a.show();
return 0;
}
【 2. 拷贝构造函数 】
- 拷贝构造函数是一种特殊的构造函数,它在创建对象时,是 使用同一类中之前创建的对象来初始化新创建的对象。
- 拷贝构造函数的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。
- 如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。
- 拷贝构造函数通常用于:
- 通过 使用另一个同类型的对象来初始化新创建的对象。
- 复制对象把它作为参数传递给函数。
- 复制对象,并从函数返回这个对象。
- 拷贝构造函数的常见形式:
classname (const classname &obj)
{
// 构造函数的主体
}
2.1 深拷贝、浅拷贝
- 浅拷贝
同一类型的对象之间可以赋值,使得 两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为 浅拷贝。一般情况下,浅拷贝没有任何副作用,但是 当类中有指针,并且指针指向动态分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。 - 深拷贝
当类中有指针,并且此指针是动态分配空间,析构函数做了 释放处理,往往需要自定义拷贝构造函数, 自行给指针动态分配空间,这种情况称为 深拷贝。
2.2 实例
2.2.1 实例1 - class作为形参
- Line line(10); // 创建一个Line类的对象line,会调用构造函数,输出 “调用构造函数”
- display(line); // 使用 line 作为参数传入display函数中时,系统首先会调用拷贝构造函数为 line 进行复制创建得到一个临时副本,输出 “调用拷贝构造函数并为指针 ptr 分配内存”
系统使用该副本在 display 函数中 进行操作,输出 “line 大小 :10”,display 函数结束后该副本作为局部变量被回收,输出 “释放内存”- 对象 Line 在主程序结束前 也将被回收,输出 “释放内存”
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 简单的构造函数
Line( const Line &obj); // 拷贝构造函数
~Line(); // 析构函数
private:
int *ptr;
};
// 构造函数
Line::Line(int len)
{
cout << "调用构造函数" << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}
// 拷贝构造函数
Line::Line(const Line &obj)
{
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}
// 析构函数
Line::~Line(void)
{
cout << "释放内存" << endl;
delete ptr;
}
// 成员函数
int Line::getLength( void )
{
return *ptr;
}
// 外部函数
void display(Line obj);
// 程序的主函数
int main( )
{
Line line(10);
display(line);
return 0;
}
// 一个外部函数
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
2.2.2 实例2 - class1=class2
- Line line1(10); // 创建一个Line类的对象line1,会调用构造函数,输出 “调用构造函数”
- Line line2 = line1; // 创建一个Line类的对象line2 且 line2=line1,即 lin2 是由 lin1 复制得到,即调用了拷贝构造函数,输出 “调用拷贝构造函数并为指针 ptr 分配内存”
- display(line1); // 使用 line1 作为参数传入display函数中时,系统首先会调用拷贝构造函数为 line1 进行复制创建得到一个临时副本,输出 “调用拷贝构造函数并为指针 ptr 分配内存”
系统使用该副本在 display 函数中 进行操作,输出 “line 大小 :10”
display 函数结束后该副本作为局部变量被回收,输出 “释放内存”- 同样地, display(line2); // 使用 line2 作为参数传入display函数中时,系统首先会调用拷贝构造函数为 line2 进行复制创建得到一个临时副本,输出 “调用拷贝构造函数并为指针 ptr 分配内存”
系统使用该副本在 display 函数中 进行操作,输出 “line 大小 :10”
display 函数结束后该副本作为局部变量被回收,输出 “释放内存”- 对象 Line1 和Line2 在主程序结束前 也将被回收,分别输出 “释放内存”
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 简单的构造函数
Line( const Line &obj); // 拷贝构造函数
~Line(); // 析构函数
private:
int *ptr;
};
// 构造函数
Line::Line(int len)
{
cout << "调用构造函数" << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}
// 拷贝构造函数
Line::Line(const Line &obj)
{
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}
// 析构函数
Line::~Line(void)
{
cout << "释放内存" << endl;
delete ptr;
}
// 成员函数
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj);
// 程序的主函数
int main( )
{
Line line1(10);
Line line2 = line1; // 这里也调用了拷贝构造函数
display(line1);
display(line2);
return 0;
}
// 外部函数
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
2.2.3 实例3 - 深拷贝构造函数
- 注意这里 为 字符串动态申请内存时,需要考虑到字符串的最后一个字符 ‘\0’:
new char [strlen(name)+1]
。
#include <iostream>
#include <cstring>
#pragma warning(disable : 4996)
using namespace std;
class Person {
public:
char* name; // 姓名
int age; // 年龄
// 构造函数
Person(const char* name, int age) {
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->age = age;
}
// 拷贝构造函数
Person(Person &p)
{
this->name=new char[strlen(p.name)+1];
strcpy(this->name,p.name);
this->age=p.age;
}
// 成员函数
void showPerson() {
cout << name << " " << age << endl;
}
// 析构函数
~Person() {
if (name != nullptr) {
delete[] name;
name = nullptr;
}
}
};
int main() {
char name[100] = { 0 };
int age;
cin >> name;
cin >> age;
Person p1(name, age);
Person p2 = p1;
p2.showPerson();
return 0;
}
【 3. 析构函数 】
-
类的析构函数是类的一种特殊的成员函数, 类的析构函数会在每次删除所创建的对象时执行。
-
析构函数的 名称与类的名称是完全相同的,只是在前面加了个 波浪号(~) 作为前缀,它 不会返回任何值,也 不能带有任何参数。
-
析构函数 有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
-
如果程序里没有构造函数和析构函数,编译器在编译的时候会自动生成构造函数和析构函数,只是函数内没有任何操作。
-
实例
#include <iostream>
using namespace std;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数声明
~Line(); // 这是析构函数声明
private:
double length;
};
// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}
Line::~Line(void)
{
cout << "Object is being deleted" << endl;
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// 程序的主函数
int main( )
{
Line line;
// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0;
}