类通常定义在函数的外面;
class Student{
public:
//成员变量
char* name;
int age;
float score;
//成员函数
void say()
{
std::cout<<"hello.\n"<
注意类定义最后有一个分号
;
,它是类定义的一部分,表示类定义结束了,不能省略。
类只是一个模板(Template),编译后不占内存空间,所以在定义类时不能队成员变量进行初始化,因为没有地方存储数据。只有在创建对象以后才会给成员变量分配内存,这个时候就可以赋值了。
Student liLei; // 创建对象
class Student LiLei // 创建对象
Student allStu[100]; // 创建对象数组
可以通过 dot(.) 方式访问类成员
Student stu;
stu.name = "小明";
stu.age = 15;
stu.score = 92.5f;
stu.say();
Student stu;
Student *pStu = &stu;
Student *pStu = new Student;
pStu -> name = "小明";
pStu -> age = 15;
pStu -> score = 92.5f;
pStu -> say();
在堆上分配内存,没有名字,只能通过一个指针指向;堆内存由程序员管理,对象使用完毕之后可以通过delete
来删除。
类的成员函数和普通函数区别:成员函数是一个类的成员,出现在类体中,它的作用范围由类来决定;而普通函数是独立的,作用范围是全局的。
::
被称为域解析符(也称作用域运算符或作用域限定符)
。inline
关键字,但是是多余的),类外定义的不会。限定符 | 类中访问限制 | 类外访问限制 |
---|---|---|
public | 可以相互访问 | 可通过对象访问 |
protected | 可以相互访问 | 不可访问 |
private | 可以相互访问 | 不可访问 |
成员访问限定符只能对类的成员进行修饰,不能修饰类,C++中的类没有共有私有之分。
构造函数是一种特殊的成员函数,它的名字和类名相同,却没有返回值,不需要用户显式调用(用户也不能调用),而是在创建对象的时候自动执行。
如下代码:
#include
using namespace std;
class Student{
private:
char* m_name;
int m_age;
float m_score;
public:
//声明构造函数
Student(char* name, int age, float score);
//声明普通函数
void display();
};
//定义构造函数
Student::Student(char* name, int age, float score){
m_name = name;
m_age = age;
m_score = score;
}
void Student::display(){
cout<< m_name << "的年龄是"<< m_age<<", 成绩是" << m_score<<endl;
}
int main()
{
//创建对象时向构造函数传递参数
Student stu("何小", 15, 92.5f);
stu.display();
//创建对象时向构造函数传参
Student *pstu = new Student("小花", 16, 98);
pstu->display();
return 0;
}
构造函数可以重载,一个类可以有多个重载的构造函数,创建对象时根据传递的实参来判断调用哪个构造函数。
构造函数的调用是强制性的,一旦在类中定义了构造函数,创建对象的时候一定要调用。如果有多个重载的构造函数,那么创建对象时提供的实参必须和其中的一个构造函数匹配;
如果用户没有自定义构造函数,编译器会自动生成一个默认的构造函数,只是这个构造函数体是空的,没有形参,也不执行任何操作:
Student(){}
调用没有参数的构造函数也可以省略括号:Student stu() == Student stu
构造函数最重要的任务便是 对成员变量进行初始化。
class Student{
private:
const char* name;
int age;
float score;
};
Student::Student(char* name, int age, float score):m_name(name),m_age(age),m_score(score){
//TODO
}
Student::Student(char* name, int age, float score):m_name(name){
m_age = age;
m_score = score;
}
const 修饰的变量只能通过 初始化列表赋值,表示不可修改。
成员变量初始化顺序与初始化列表中列出的变量顺序无关,只与成员变量在类中的声明顺序有关
与构造函数相对立的就是析构函数,用来释放分配的内存、关闭打开的文件等。
析构函数(Destruction)也是一种特殊的成员函数,没有返回值,不需要程序员显示调用(程序员也没法显式调用),而是在销毁对象的时候自动执行
Student(int len); // 构造函数
~Student(); // 析构函数
#include
using namespace std;
class VLA{
public:
VLA(int len); //构造函数
~VLA(); //析构函数
};
VLA::VLA(int len):m_len(len){
if(len > 0)
m_arr = new int[len]; //分配内存
else
m_arr = null;
}
VLA::~VLA(){
delete[] m_arr; //释放内存
}
在构造函数有多个参数时,数组的初始化列表中要显式包含对构造函数的调用,例如:
class CTest{
public:
CTest(int n){} //构造函数(1)
CTest(int n, int m){} //构造函数(2)
CTest(){} //构造函数(3)
};
int main(){
// 三个元素分别用构造函数(1)(2)(3)初始化
CTest array1[3] = {1, CTest(1, 2)};
// 三个元素分别用构造函数(2)(2)(1)初始化
CTest array2[3] = {CTest(2, 3), CTest(1, 2), 1};
//两个元素指向的对象分别用构造函数(1)(2)初始化
CTest* pArray[3] = {new CTest(4), new CTest(1,2)};
return 0;
}
一个类的成员如果是另一个类的对象,就称之为“成员对象”。包含成员对象的类叫封闭类(enclosed class)
#include
using namespace std;
// 轮胎类
class Tyre{
private:
int m_width;
int m_radius;
public:
Tyre(int width, int radius);
void display();
};
Tyre::Tyre(int width, int radius):m_width(width), m_radius(radius){}
void Tyre::display(){
cout<< "tyre width is"<< m_width <<"\n" << "tyre radius is" << m_radius<<endl;
}
// 引擎类
class Engine{
private:
float m_displacement;
public:
Engine(float displacement = 12.0);
void display();
};
Engine::Engine(float displacement):m_displacement(displacement){}
void Engine::display(){
cout<< "引擎大小为"<<m_displacement<<"\n"<<endl;
}
// 汽车类
class Car{
private:
float m_price;
public:
Tyre my_tyre;
Engine my_engine;
public:
Car(int width, int radius, float price);
void display();
};
// 指明m_tyre对象的初始化方式*/{ };
Car::Car(int width, int radius, float price):my_tyre(width, radius){
m_price = price;
}
void Car::display(){
cout << "价格:" << this->m_price << "¥" << endl;
this->my_tyre.display();
this->my_engine.display();
}
int main(int argc, char** argv){
Car car(200, 300, 500000);
car.display();
return 0;
}
在上面的程序中,如果 Car 类的构造函数没有初始化列表,那么第 51 行就会编译出错,因为编译器不知道该如何初始化 car.m_tyre 对象,因为 Tyre 类没有无参构造函数,而编译器又找不到用来初始化 car.m_tyre 对象的参数。
封闭类对象生成时,先执行所有成员对象的构造函数,然后执行封闭类自己的构造函数。成员对象构造函数的执行次序和成员对象在类定义中的次序一致,与他们在构造函数初始化列表中出现的次序无关。
当封闭类对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数,成员对象析构函数的执行次序和构造函数的执行次序相反,即先构造的后析构。
#include
using namespace std;
class Typr{
public:
Typr(){cout<< "typr constru" <<endl;}
~Typr(){cout<< "typr destru" <<endl;}
};
class Engine{
public:
Engine(){cout<< "Engine constru" <<endl;}
~Engine(){cout<< "Engine destru" <<endl;}
};
class Car{
private:
Typr m_typr;
Engine m_engine;
public:
Car(){cout<< "Car constru" <<endl;}
~Car(){cout<< "Car destru" <<endl;}
};
int main()
{
Car *car = new Car;
delete car;
return 0;
}
运行结果
typr constru
Engine constru
Car constru
Car destru
Engine destru
typr destru