类的基本知识已经学过了,但是对类的理解还不够深刻,下面摘一些C++远征中的讲解。
类的实例化有两种,一种是从栈中,一种是从堆中。
我们定义一个TV类。
class TV
{
public:
char name[20];
int type;
void changeVol();
void power();
};
从栈中实例化对象。系统会自动回收内存。
TV tv;
TV tv[20];//对象的数组
从堆中实例化对象。需要我们手动的释放内存。
<span style="white-space:pre"> </span>TV *p = new TV();
TV *q = new TV[20];
delete p;
delete[]q;
一个类的数组访问成员。
int main()
{
TV *p= new TV[5];
for (int i = 0; i < 5; i++)
{
p[i]->type = 0;
p[i]->changeVol();
}
delete []p;
p = NULL;
system("pause");//暂停程序
return 0;
}
类外定义,内联函数使用关键词inline来定义,下面是一个例子。
inline void fun()
{
cout << "hello " << endl;
}
其中,区别如下,内联省去了调用和返回。但是要求内联函数是逻辑简单的函数。
在类定义中,将类函数的函数体定义在类内是,就是类内定义。类内定义就是默认为inline内联函数。除了类内定义,当然就有类外定义,就是函数体在类外面。其中有两种情况,同文件类外定义和份文件类外定义。
类内定义如下,在同一个Car.cpp文件下,类的定义和函数全部定义在一个文件中。
class Car
{
public:
void fun();
void stop();
void changeSpeed();
};
void Car::run(){}
void Car::stop(){}
void Car::changeSpeed(){}
实际专业的程序员都会将类定义分文件完成,这样做有很多好处。以下为一个示例,在Car.h中定义类,在Car.cpp中定义函数。
数据的结构,在内存中,分区如图所示。栈区由系统管理,分配和回收不需要程序员关心。
在申明一个类后,不会占用存储。只有在实例化一个对象时,才会在堆或栈中占据空间。若没有对实例化的数据初始化,是非常危险的。所以要对其初始化。为了防止漏初始化和重复初始化,C++设置了构造函数,在对象实例化时自动调用,被调用且仅调用一次,构造函数与类名是相同的,构造函数没有返回值,可以重载,当用户没有定义构造函数时,系统自动生成一个构造函数,如下代码所示。
class Student()
{
public:
Student(){ m_strName = "jim"; }//无参数的构造函数
private:
string m_strName;
}
有参数的构造函数下。
class Student()
{
public:
Student(string name){ m_strName = name; }
private:
string m_strName;
}
当然,构造函数也是可以重载的。
class Student()
{
public:
Student(){ m_strName = "Jim"; }
Student(string name){ m_strName = name; }//重载的构造函数
private:
string m_strName;
}
初始化列表语法即下面的代码。有几个特性,初始化列表优先于构造函数执行,初始化列表只能用于构造函数,初始化列表可以同时初始化多个数据成员。
class Student()
{
public:
Student() :m_strName("Jim"), m_iAge(10){}//初始化列表
private:
string m_strName;
int m_iAge;
}
初始化列表的必要性。
//初始化列表的必要性
class Circle
{
public:
Circle(){m_dPi=3.14}//错误,因为是const,不能再复赋值
private:
const double m_dPi;
};
class Circle
{
public:
Circle():m_dPi=3.14{ }//正确
private:
const double m_dPi;
};
下面首先看一段代码。
class Student
{
public:
Student()
{
cout << "Student" << endl;
}
private:
string m_strName;
};
int main()
{
Student stu1;
Student stu2 = stu1;
Student stu3(stu1);
return 0;
}
在main()函数初始化中,将stu1的复制给了stu2和stu3,理论上应该是初始化了三次,那么应该输出三行“student”,但实际运行后法相,只输出一行“student”。这两种实例化方式时调用的是拷贝构造函数,形式如下。在没有定义拷贝构造函数时,系统会自动生成一个拷贝构造函数并调用,这也是为什么上面代码只输出了一行“student”。
class Student
{
public:
Student(){ m_strName = "Jim"; }
Student(const Student& stu){}
private:
string m_strName;
};
析构函数在对象销毁时自动调用,归还系统资源,格式为~类名()。
class Student
{
public:
Student(){ cout << "Student" << endl; }
~Student(){ cout << "~Student" << endl; }
private:
string m_strName;
};
下面一段代码展示了析构函数的重要性。析构函数可以释放掉堆中的内存。
class Student
{
public:
Student(){ m_pName = new char[20]; }//堆中申请了资源
private:
char *m_pName;
};