一、对象初始化
如何给对象进行初始化:
为每个类都提供一个public的initialize函数,对象创建后立即调用initialize函数进行初始化
#include<iostream>
using namespace std;
class student
{
private:
int i;
public:
void initialize()
{
i = 5;
}
int get()
{
return i;
}
};
int main()
{
student t1;
t1.initialize();//定义对象后需马上调用初始化函数
cout<<t1.get()<<endl;
cin.get();
return 0;
}
能工作了,但不爽:
initialize只是一个普通的函数,必须显示的调用
一旦失误,对象没有初始化,那么结果是不确定的(没有初始化的对象,其内部成员变量的值是不确定的)
如上面的程序,创建对象后不调用initialize函数会输出随机值
二、c++里的构造函数(与类名相同,可以有参数但无返回值)
没有返回值和返回值是void是完全不同的
1.构造函数的调用:
一般情况下c++编译器会自动调用构造函数,但在一些情况下则需要手工调用构造函数
#include<iostream>
using namespace std;
class student
{
private:
int i;
public:
student(int v)//构造函数,可以有参数,但无返回值
{
i = v;
}
int get()
{
return i;
}
};
int main()
{
student t1(3);//自动初始化
student t2=4;//自动初始化
student t3 = student(5);//手动初始化
cout<<t1.get()<<endl;//3
cout<<t2.get()<<endl;//4
cout<<t3.get()<<endl;//5
cin.get();
return 0;
}
三种方法无本质区别
*必须手动初始化的情况:
Test tA[3] = {t1(3),t2(4),t3(5)};这是一个类数组*
三、成员函数的重载:
类的成员函数一样可以重载,并遵守相同的重载规则,构造函数也可以重载(调用重载构造函数时也遵循规则)
#include<iostream>
using namespace std;
class student
{
private:
int i;
int j;
int k;
public:
student(int v)//构造函数,可以有参数,但无返回值
{
i = v;
j = v;
k = v;
}
student()
{
i = 3;
j = 4;
k = 5;
}
void print()//普通的成员函数
{
cout<<i<<endl;
cout<<j<<endl;
cout<<k<<endl;
}
void print(int v)
{
cout<<i<<endl;
cout<<j<<endl;
cout<<k<<endl;
cout<<"hello"<<endl;
}
};
int main()
{
student t1;//调用的构造函数是student()
student t2(8);//调用的构造函数是student(int v)
t2.print();
t1.print(5);//调用的是void print(int v),因为有参数传进去
t1.print();//调用的是void print(),因为没有参数传进去
cin.get();
return 0;
}
student tA[3];//为何此处没有初始化也可以呢,因为类里的student()没有参数,就默认调用这个函数了
四、两个特殊的构造函数(无参构造函数和拷贝构造函数)
1.无参构造函数:
当类中没有定义构造函数时,编译器提供一个无参构造函数,并且其函数体为空
2.拷贝构造函数:
当类中没有定义拷贝函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量成员变成员变量的值的复制
#include<iostream>
using namespace std;
class Test
{
private:
int a;
public:
Test(int b)//构造函数
{
a = b;
}
void show()
{
cout<<a<<endl;
}
};
int main()
{
Test t1(100);
Test t2 = t1;
t2.show();//打印100,调用了默认的拷贝构造函数,将t1里的值拷贝到了t2
cin.get();
return 0;
}
#include<iostream>
using namespace std;
class Test
{
private:
int a;
public:
Test(int b)//构造函数
{
a = b;
}
Test(const Test& obj) //拷贝构造函数的形态
{
cout<<"hello"<<endl;
}
void show()
{
cout<<a<<endl;
}
};
int main()
{
Test t1(100);
Test t2 = t1; //打印出hello,调用了自定义的拷贝构造函数
t2.show();//打印乱码,有了自定义的拷贝构造函数,不再调用默认的,而自定义里面的拷贝构造函数没有将a的值拷贝过来,故 cin.get();// 输出乱码
return 0;
}
五、数组类的创建
Array.h
#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array
{
private:
int mlenth;
int *mspace;
public:
Array(int lenth);
int Lenth();
void SetData(int index, int data);
int GetData(int index);
void Destroy();
};
#endif
Array.cpp
#include"Array.h"
Array::Array(int lenth)
{
if(0 > lenth)
{
lenth = 0;
}
else
{
mlenth = lenth;
mspace = new int[lenth];
}
}
int Array::Lenth()
{
return mlenth;
}
void Array::SetData(int index, int data)
{
mspace[index] = data;
}
int Array::GetData(int index)
{
return mspace[index];
}
void Array::Destroy()
{
mlenth = -1;
delete[] mspace;
}
#include<iostream>
#include"Array.h"
using namespace std;
int main()
{
Array a1(10);
for(int i = 0; i < 10; i++)
{
a1.SetData(i, i);
}
for( int i = 0; i < 10; i++ )
{
cout<<a1.GetData(i);
}
Array a2(10);
for(int i = 0; i < 10; i++)
{
a2.SetData(i, i);
}
for( int i = 0; i < 10; i++ )
{
cout<<a2.GetData(i);
}
a1.Destroy();
a2.Destroy();
cin.get();
return 0;
}
*注:以上代码有一个非常严重的bug,a1.Destroy()和a2.Destroy();对同一片内存释放了两次
因为使用默认的拷贝构造函数只是简单的值拷贝
解决方法:自定义一个拷贝构造函数:*
#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array
{
private:
int mlenth;
int *mspace;
public:
Array(int lenth);
Array(Array const& obj);//自定义一个拷贝函数
int Lenth();
void SetData(int index, int data);
int GetData(int index);
void Destroy();
};
#endif
#include"Array.h"
Array::Array(int lenth)
{
if(0 > lenth)
{
lenth = 0;
}
else
{
mlenth = lenth;
mspace = new int[lenth];
}
}
Array::Array(Array const& obj)
{
mlenth = obj.mlenth;
mspace = new int[mlenth];//自定义的拷贝函数里边又申请了一片内存
if(mlenth > 0)
{
for(int i = 0; i<mlenth; i++)
{
mspace[i] = obj.mspace[i];
}
}
}
int Array::Lenth()
{
return mlenth;
}
void Array::SetData(int index, int data)
{
mspace[index] = data;
}
int Array::GetData(int index)
{
return mspace[index];
}
void Array::Destroy()
{
mlenth = -1;
delete[] mspace;
}
#include<iostream>
#include"Array.h"
using namespace std;
int main()
{
Array a1(10);
for(int i = 0; i < 10; i++)
{
a1.SetData(i, i);
}
for( int i = 0; i < 10; i++ )
{
cout<<a1.GetData(i);
}
Array a2(10);
for(int i = 0; i < 10; i++)
{
a2.SetData(i, i);
}
for( int i = 0; i < 10; i++ )
{
cout<<a2.GetData(i);
}
a1.Destroy();
a2.Destroy();
cin.get();
return 0;
}
六、c++中的类可以使用其它类来定义成员变量
#include<iostream>
using namespace std;
class M
{
private:
int a;
public:
M()
{
a = 5;
}
int get()
{
return a;
}
};
class N
{
private:
M a1;//类N的成员变量是其他类的对象
public:
void print()
{
cout<<a1.get()<<endl;//成功打印出5
}
};
int main()
{
N a2;
a2.print();
cin.get();
return 0;
}
七、初始化列表(对成员变量进行初始化)
注:成员变量的初始化顺序与声明有关,与在初始列表中的顺序无关
初始化列表先于构造函数的函数体执行
#include<iostream>
using namespace std;
class M
{
private:
int a;
public:
M(int i)
{
a = i;
cout<<"hello1"<<endl;
}
int get()
{
return a;
}
};
class N
{
private:
M a1;//先声明a1
const int c;
public:
N():c(5),a1(10)//注意参数列表的格式,且参数列表的执行顺序与声明时的顺序有关,参数列表先于构造函数执行
{
cout<<"hello2"<<endl;
}
};
int main()
{
N a2;
cin.get();
return 0;
}
注:
类中的const成员是肯定会被分配空间的
类中的const成员变量只是一个只读变量
编译器无法直接得到const成员变量的初始值,因此无法进入符号表成为真正意义上的常量
初始化与赋值不同:
初始化是用已存在的对象或值对 正在创建的对象 进行初值设置
赋值是用已存在的对象或值对 已经存在的对象 进行值设置
七、对象的销毁
那么如何进行对象的销毁?
解决方法:
(1).为每个类都提供一个public的destroy函数
对象不再需要时需要立即调用destroy函数进行清理(如前面的数组类的创建的例子)
但是类似的问题:
destroy只是一个普通的函数,必须显示的调用
如果对象销毁前没有做清理工作,那么很可能造成资源的泄露
(2). c++的类定义可以定义一个特殊的成员函数清理对象
这个特殊的成员函数叫做析构函数
定义:~ClassName()
析构函数没有任何的参数也没有任何的返回值类型
析构函数在对象被销毁时自动被调用
#include <stdio.h>
class Test
{
private:
int mI;
public:
Test(int i) : mI(i)
{
printf("Test(), mI = %d\n", mI);
}
~Test()//析构函数
{
printf("~Test(), mI = %d\n", mI);
}
};
void run()
{
Test t1(1);
Test t2(2);
}
int main()
{
run();
printf("Press any key to continue...");
getchar();
return 0;
}
八:数组类的进化
将Destroy()函数改成析构函数即可,程序执行完会自动调用
九:构造与析构函数的调用秩序
当类中有成员变量是其他类的对象时
首先调用成员变量的构造函数,且调用顺序与声明顺序相同
之后调用自身类的构造函数
析构函数的调用秩序与对应的构造函数调用秩序相反
#include<iostream>
using namespace std;
class M
{
private:
int a;
public:
M(int i)
{
a = i;
cout<<"hello1"<<endl;
}
int get()
{
return a;
}
};
class G
{
public:
G(int i)
{
cout<<"hello3"<<endl;
}
};
class N
{
private:
M a1;
G a2;
const int c;
public:
N():c(5),a2(10),a1(1)
{
//cout<<c<<endl;//说明参数列表先于构造函数执行
//c = 6;//发生错误,说明c是只读变量
cout<<"hello2"<<endl;
}
};
int main()
{
N a2;
cin.get();
return 0;
}
先打印hello1,再打印hello2,最后是hello3
十、是否可以直接调用构造函数?
可以直接调用,会得到一个临时对象,但是这个临时对象马上就被析构了
构造函数只能被编译器自动调用或者声明一个对象时手工调用
如落不然会生成了一个临时对象,那行结束后马上被析构
#include <stdio.h>
class Test
{
private:
int mI;
int mJ;
const char* mS;
public:
Test()
{
printf("Test()\n");
mI = 0;
mJ = 0;
}
Test(const char* s)
{
printf("Test(const char* s)\n");
Test();//这行时生成一个临时对象然后马上就被销毁了
mS = s;
}
~Test()
{
printf("~Test()\n");
}
void print()
{
printf("mI = %d, mJ = %d, mS = %s\n", mI, mJ, mS);
}
};
void run()
{
Test t = Test("Delphi Tang"); // Test t("Delphi Tang");
t.print();
}
int main()
{
run();
printf("Press any key to continue...");
getchar();
return 0;
}