缺省参数的成员函数
带默认参数的定义函数中默认参数从右向左填数,而在函数的使用时,参数从左到右传递,如下,默认b = 10,在函数调用a2.Set(20.0);中,20.0对应a的数值,故sum = 30
详细C++代码如下:
#include<bits/stdc++.h>
using namespace std;
class A{
float x, y;
public:
float Sum(void)
{
return x + y;
}
void Set(float a, float b = 10.0)
{
x = a;
y = b;
}
void Print()
{
cout << "x = " << x << '\t' << "y = " << y << endl;
}
};
int main()
{
A a1, a2;
a1.Set(2.0, 4.0);
cout << "a1:";
a1.Print();
cout << "a1.sum = " << a1.Sum() << endl;
a2.Set(20.0);
cout << "a2:";
a2.Sum();
cout << "a2.sum = " << a2.Sum() << endl;
return 0;
}
运行结果如下:
定义类的指针以及用指针调用对象
详细C++代码如下
#include<bits/stdc++.h>
using namespace std;
class A{
float x, y;
public:
float Sum(void)
{
return x + y;
}
void Set(float a, float b)
{
x = a;
y = b;
}
void Print()
{
cout << "x = " << x << '\t' << "y = " << y << endl;
}
};
int main()
{
A a1, a2;
A *p;
p = &a1;
p->Set(2.0, 3.0);
p->Print();
cout << p->Sum() << endl;
a2.Set(10.0, 20.1);
a2.Print();
return 0;
}
运行结果如下:
返回引用类型的成员函数( 把引用作为返回值)
通过使用引用来替代指针,会使C++程序更容易阅读和维护。C++函数可以返回一个引用,方式与返回一个指针类似。当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。例如,请看下面这个简单的程序:
#include<iostream>
using namespace std;
int a[] = {2, 6, 8, 9, 10};
int & value(int i)
{
return a[i]; //返回第i个元素的引用
}
int main()
{
int i;
cout << "改变前的值:" << endl;
for(i = 0; i < 5; i++)
{
cout << "a[" << i << "] = ";
cout << a[i] << endl;
}
value(1) = 20;
value(3) = 20;
cout << "改变后的值:" << endl;
for(i = 0; i < 5; i++)
{
cout << "a[" << i << "] = ";
cout << a[i] << endl;
}
return 0;
}
运行结果如下:
代码分析:
本例先定义一个全局的数组a,并初始化赋值。然后定义一个int型的引用函数value(),并且定义了一个整型的形参i,所以该函数的返回值也应该是引用类型的数据。在main()函数中,使用for循环,先遍历数组a。然后通过调用函数value(),修改数组a中的元素,并且返回第i个元素的引用。最后通过for循环再次变量数组a。
第二个类似的C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
float &Getx()
{
return x; //返回x的引用
}
void Set(float a, float b)
{
x = a;
y = b;
}
void Print()
{
cout << x << '\t' << y << endl;
}
};
int main()
{
A a1, a2;
a1.Set(3, 5);
cout << "a1:";
a1.Print();
a1.Getx() = 30; //向a1对象中的私有成员x赋值
cout << "\nchanged a1:";
a1.Print();
return 0;
}
运行结果如下:
构造函数与析构函数概述
由类得到对象,需要构造函数,系统会自动调用相应的构造函数;
对象使用完后需要释放占有的资源,系统就会自动调用相应的析构函数。
构造函数的定义
构造函数可以带参数(构造函数是在创建对象时,使用给定的值来将对象初始化)、可以重载,同时没有返回值。
构造函数是类的成员函数,系统约定构造函数名必须与类名相同。构造函数提供了初始化对象的一种简单的方法
Attention!!!
对构造函数,说明以下几点:
- 1、构造函数的函数名必须与类名相同。构造函数的主要作用是完成初始化对象的数据成员以及其它的初始化工作。
- 2、在定义构造函数时,不能指定函数返回值的类型,也不能指定为void类型。
- 3、 一个类可以定义若干个构造函数。当定义多个构造函数时,必须满足函数重载的原则。
- 4、构造函数可以指定参数的缺省值。
- 5、若定义的类要说明该类的对象时,构造函数必须是公有的成员函数。如果定义的类仅用于派生其它类时,则可将构造函数定义为保护的成员函数。
由于构造函数属于类的成员函数,它对私有数据成员、保护的数据成员和公有的数据成员均能进行初始化。
Attentinon!!!
类的数据成员是不能在声明类时初始化的。
对局部对象,静态对象,全局对象的初始化:
- 对于局部对象,每次定义对象时,都要调用构造函数。
- 对于静态对象,是在首次定义对象时,调用构造函数的,且由于对象一直存在,只调用一次构造函数。
- 对于全局对象,是在main函数执行之前调用构造函数的。
综合例子:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(float a, float b)
{
x = a;
y = b;
cout << "初始化局部对象" << endl;
}
A()
{
x = 0;
y = 0;
cout << "初始化静态局部对象" << endl;
}
A(float a)
{
x = a;
y = 0;
cout << "初始化全局对象" << endl;
}
void Print()
{
cout << x << '\t' << y << endl;
}
};
A a0(100.0); //初始化全局对象
void f()
{
cout << "进入f()函数" << endl;
A a2(1, 2);
static A a3; //a3静态对象
}
int main()
{
cout << "进入main函数" << endl;
A a1(3.0, 7.0); //初始化局部对象
f(); //进入f()函数,初局,初静
f(); //进入f()函数,初局
return 0;
}
运行结果如下:
缺省的构造函数
在定义类时,若没有定义类的构造函数,则编译器自动产生一个缺省的构造函数,其格式为:className::className() { }
缺省的构造函数并不对所产生对象的数据成员赋初值;即新产生对象的数据成员的值是不确定的。
注:
关于缺省的构造函数,说明以下几点:
- 1、在定义类时,只要显式定义了一个类的构造函数,则编译器就不产生缺省的构造函数
- 2、所有的对象在定义时,必须调用构造函数
- 3、在类中,若定义了没有参数的构造函数,或各参数均有缺省值的构造函数也称为缺省的构造函数,缺省的构造函数只能有一个
- 4、产生对象时,系统必定要调用构造函数。所以任一对象的构造函数必须唯一
无参构造函数和全部参数都指定了默认值的构造函数,二者不可兼得
Box();
Box(int = 10, int = 10, int = 10);
构造函数与new运算符
可以使用new运算符来动态地建立对象。建立时自动调用构造函数,以便完成初始化对象的数据成员。最后返回这个动态对象的起始地址。
用new运算符产生的动态对象,在不再使用这种对象时,必须用delete运算符来释放对象所占用的存储空间。
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(float a, float b)
{
x = a;
y = b;
}
A()
{
x = 0;
y = 0;
}
void Print()
{
cout << x << '\t' << y << endl;
}
};
int main()
{
A *p1, *p2;
p1 = new A(3.1, 5.2);
p2 = new A;
p1->Print();
p2->Print();
delete p1;
delete p2;
return 0;
}
运行结果如下:
完成拷贝功能的构造函数
可以在定义一个对象的时候用另一个对象为其初始化,即构造函数的参数是另一个对象的引用,这种构造函数常为完成拷贝功能的构造函数。
完成拷贝功能的构造函数的一般格式为:
ClassName::ClassName(ClassName &<变量名>)
{ …
// 函数体完成对应数据成员的赋值
}
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(float a = 0, float b = 0)
{
x = a;
y = b;
}
A(A &a)
{
x = a.x;
y = a.y;
}
void Print()
{
cout << x << '\t' << y << endl;
}
};
int main()
{
A a1(1.2, 3.3);
A a2(a1);
a2.Print();
return 0;
}
运行结果如下:
隐含的完成拷贝功能的构造函数
如果没有定义完成拷贝功能的构造函数,编译器自动生成一个隐含的完成拷贝功能的构造函数,依次完成类中对应数据成员的拷贝。
由编译器为每个类产生的这种隐含的完成拷贝功能的构造函数,依次完成类中对应数据成员的拷贝。
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(float a = 0, float b = 0)
{
x = a;
y = b;
cout << "调用了构造函数" << endl;
}
void Print()
{
cout << x << '\t' << y << endl;
}
~A()
{
cout << "调用了析构函数" << endl;
}
};
int main()
{
A a1(1.2, 2.3);
A a2(a1);
A a3 = a1;
a1.Print();
a2.Print();
a3.Print();
return 0;
}
运行结果如下:
当类中的数据成员中使用new运算符,动态地申请存储空间进行赋初值时,必须在类中显式地定义一个完成拷贝功能的构造函数,以便正确实现数据成员的复制。
示例C++的A代码如下:
#include<bits/stdc++.h>
using namespace std;
class Str{
int Length;
char *Sp;
public:
Str(char *string)
{
if(string)
{
Length = strlen(string);
Sp = new char[Length + 1];
strcpy(Sp, string);
}
else
Sp = 0;
}
void Show()
{
cout << Sp << endl;
}
~Str()
{
if(Sp)
delete []Sp;
}
};
int main()
{
Str s1("Study C++");
Str s2(s1);
s1.Show();
s2.Show();
return 0;
}
运行结果如下:
代码分析如下:
示例C++的B代码如下:
#include<bits/stdc++.h>
using namespace std;
class Str{
int Length;
char *Sp;
public:
Str(char *string)
{
if(string)
{
Length = strlen(string);
Sp = new char[Length + 1];
strcpy(Sp, string);
}
else
Sp = 0;
}
Str(Str &s)
{
if(s.Sp)
{
Length = s.Length;
Sp = new char[Length + 1];
strcpy(Sp, s.Sp);
}
else
Sp = 0;
}
void Show()
{
cout << Sp << endl;
}
~Str()
{
if(Sp)
delete []Sp;
}
};
int main()
{
Str s1("Study C++");
Str s2(s1);
s1.Show();
s2.Show();
return 0;
}
运行结果如下:
代码分析:
对比A、B两个代码,我们可以发现显示定义拷贝函数的优势
构造函数与对象成员
对类A的对象初始化的同时还要对其成员数据类B的对象进行初始化,所以,类A的构造函数中要调用类B的构造函数。
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(int a, int b)
{
x = a;
y = b;
}
void Show()
{
cout << "x = " << x << '\t' << "y = " << y << endl;
}
};
class C{
float z;
A a1; //组合
public:
C(int a, int b, int c):a1(b, c)
{
z = a;
}
void Show()
{
cout << "z = " << z << endl;
a1.Show();
}
};
int main()
{
C c1(1, 2, 3);
c1.Show();
return 0;
}
运行结果如下:
ClassName::ClassName(args):c1(args1),…,cn(agrsn)
{
… //对其它成员的初始化
}
初始化对象成员的参数(实参)可以是表达式,也可以仅对部分对象成员进行初始化。
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(int a, int b)
{
x = a;
y = b;
}
void Show()
{
cout << "x = " << x << '\t' << "y = " << y << endl;
}
};
class B{
float x1, y1;
public:
B(int a, int b)
{
x1 = a;
y1 = b;
}
void Show()
{
cout << "x1 = " << x1 << '\t' << "y1 = " << y1 << endl;
}
};
class C{
float z;
A a1;
B b1;
public:
C(int a, int b, int c, int d, int e):a1(a + b, c), b1(a, d)
{
z = e;
}
void Show()
{
cout << "z = " << z << endl;
a1.Show();
b1.Show();
}
};
int main()
{
C c1(1, 2, 3, 4, 5);
c1.Show();
return 0;
}
运行结果如下:
对对象成员的构造函数的调用顺序取决于这些对象成员在类中说明的顺序,与它们在成员初始化列表中的顺序无关。
当建立类ClassName的对象时,先调用各个对象成员的构造函数,初始化相应的对象成员,然后才执行类ClassName的构造函数,初始化类ClassName中的其它成员。析构函数的调用顺序与构造函数正好相反。
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x;
public:
A(int a)
{
x = a;
cout << "调用了A的构造函数" << endl;
}
~A()
{
cout << "调用了A的析构函数" << endl;
}
};
class B{
float y;
public:
B(int a)
{
y = a;
cout << "调用了B的构造函数" << endl;
}
~B()
{
cout << "调用了B的析构函数" << endl;
}
};
class C{
float z;
B b1;
A a1;
public:
C(int a, int b, int c):a1(a), b1(b)
{
z = c;
cout << "调用了C的构造函数" << endl;
}
~C()
{
cout << "调用了C的析构函数" << endl;
}
};
int main()
{
C c1(1, 2, 3);
return 0;
}
运行结果如下:
析构函数
析构函数的作用与构造函数正好相反,是在对象的生命期结束时,释放系统为对象所分配的空间,即撤消一个对象。
析构函数也是类的成员函数,定义析构函数的格式为:
ClassName::~ClassName( )
{
// 函数体;
}
析构函数的特点如下:
- 1、析构函数是成员函数,函数体可写在类体内,也可写在类体外。
- 2、析构函数是一个特殊的成员函数,函数名必须与类名相同,并在其前面加上字符“~”,以便和构造函数名相区别。
- 3、析构函数不能带有任何参数,不能有返回值,不指定函数类型。
- 4、一个类中,只能定义一个析构函数,析构函数不允许重载。
- 5、析构函数是在撤消对象时由系统自动调用的。
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(float a, float b)
{
x = a;
y = b;
cout << "调用非缺省的构造函数" << endl;
}
A()
{
x = 0;
y = 0;
cout << "调用缺省的构造函数" << endl;
}
~A()
{
cout << "调用析构函数" << endl;
}
void Print()
{
cout << x << '\t' << y << endl;
}
};
int main()
{
A a1;
A a2(3.2, 33.3);
cout << "退出主函数" << endl;
return 0;
}
运行结果如下:
在程序的执行过程中,对象如果用new运算符开辟了空间,则在类中应该定义一个析构函数,并在析构函数中使用delete删除由new分配的内存空间。因为在撤消对象时,系统自动收回为对象所分配的存储空间,而不能自动收回由new分配的动态存储空间。
示例C++代码如下:
#include<bits/stdc++.h>
using namespace std;
class Str{
char *Sp;
int Len;
public:
Str(char *string)
{
if(string)
{
Len = strlen(string);
Sp = new char[Len + 1];
strcpy(Sp, string);
}
else
Sp = 0;
}
void Show()
{
cout << Sp << endl;
}
~Str()
{
if(Sp)
{
delete []Sp;
cout << "释放对象回收空间" << endl;
}
}
};
int main()
{
Str s1("Study C++");
s1.Show();
return 0;
}
运行结果如下:
对于用new运算符动态生成的对象,在产生对象时调用构造函数,只有使用delete运算符来释放对象时,才调用析构函数。若不使用delete来撤消动态生成的对象,程序结束时,对象仍存在,并占用相应的存储空间,即系统不能自动地调用析构函数来撤消动态生成的对象。
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(float a, float b)
{
x = a;
y = b;
cout << "调用构造函数" << endl;
}
void Print()
{
cout << x << '\t' << y << endl;
}
~A()
{
cout << "调用了析构函数" << endl;
}
};
int main()
{
cout << "进入main()函数" << endl;
A *p1;
p1 = new A(3.1, 5.2);
p1->Print();
delete p1;
cout << "退出main()函数" << endl;
return 0;
}
运行结果如下:
下面的C++例子类似本文中关于构造函数的第一个C++例子,为了让大家对析构函数、构造函数有一个更好的理解
详细C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(float a, float b)
{
x = a;
y = b;
cout << "初始化自动局部对象" << endl;
}
A()
{
x = 0;
y = 0;
cout << "初始化静态局部对象" << endl;
}
A(float a)
{
x = a;
y = 0;
cout << "初始化全局对象" << endl;
}
~A()
{
cout << "调用析构函数" << x << endl;
}
};
A a0(100.3); //初始化全局变量
void f()
{
A ab(10.2, 20.3);
static A a3;
}
int main()
{
cout << "进入main函数" << endl;
f(); //初始化自动局部对象,初始化静态局部对象,调用析构函数10.2
f(); //初始化自动局部对象,调用析构函数10.2,调用析构函数0
return 0; //调用析构函数100.3
}
运行结果如下:
动态构造及析构对象数组
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(float a = 0, float b = 0)
{
x = a;
y = b;
cout << "调用了构造函数" << endl;
}
void Print()
{
cout << x << '\t' << y << endl;
}
~A()
{
cout << "调用了析构函数" << endl;
}
};
int main()
{
cout << "进入main()函数" << endl;
A *p1;
p1 = new A[3];
cout << "\n完成开辟数组空间\n" << endl;
delete []p1;
cout << "退出main()函数" << endl;
return 0;
}
运行结果如下:
若在类的定义中没有显式地定义析构函数时,则编译器自动地产生一个缺省的析构函数,其格式为:
ClassName::~ClassName() { };
要释放对象的数据成员中用new运算符分配的动态空间时,必须显式地定义析构函数
实现类型转换的构造函数
示例C++代码如下:
#include<iostream>
using namespace std;
class A{
float x, y;
public:
A(float a, float b)
{
x = a;
y = b;
cout << "调用构造函数" << endl;
}
~A()
{
cout << "调用析构函数" << x << endl;
}
void Print()
{
cout << x << '\t' << y << endl;
}
};
int main()
{
A a1(1.2, 10.3);
a1.Print();
a1 = A(3.3, 30.3); //产生临时对象,初始化并赋值后立即释放
a1.Print();
cout << "退出主函数" << endl;
return 0;
}
运行结果如下:
注:当构造函数只有一个参数时,可以用= 强制赋值
示例C++代码如下:
#include<iostream>
using namespace std;
class B{
float x;
public:
B(float a)
{
x = a;
cout << "调用构造函数" << endl;
}
~B()
{
cout << "调用析构函数" << x << endl;
}
void Print()
{
cout << x << endl;
}
};
int main()
{
B b1(1.3);
b1.Print();
B b2 = 100;
b2.Print();
b1 = 10; //单参数可以这样赋值相当于b1 = B(10)
b1.Print();
cout << "退出主函数" << endl;
return 0;
}
运行结果如下: