类
- 类是一种自定义类型,类似于结构体。
- 类将数据表示和操纵数据的方法组合成一个整洁的包。
类的组成
- 类声明:以数据成员的方式描述数据部分,以成员函数(被称为方法)的方式描述公有接口。
- 类方法定义:描述如何实现类成员函数。
类声明
class Student // 关键字class标识类定义
{
// 默认是私有的
int m_math; // 私有成员
private: // 私有成员
std::string m_name; // 成员变量
int m_English;
int m_Chinese;
int m_sum;
void set_sum() // 内联方法
{
m_sum = m_math + m_English + m_Chinese;
}
public: // 公有成员
Student(); // 默认构造函数
Student(std::string name) // 构造函数
{
m_name = name;
}
Student(std::string name, int math, int English, int Chinese); // 构造函数
void set_scores(int math, int English, int Chinese); // 公有成员函数
double get_average() const; // const成员函数
void show() const; // const成员函数
~Student(); // 析构函数
};
说明
- 关键字class表示类定义。
- 访问控制: 关键字private和public描述了对类成员的访问控制
- public: 使用类对象的程序可以直接访问公有部分数据
- private: 只能通过公有成员函数或者友元函数来访问私有部分数据
- 可以不必在类声明中使用关键字private,因为类对象的默认访问控制是private。
实现类成员函数
- 定义成员函数时,使用作用域解析运算符::来标识函数所属的类。
- 类方法可以访问类的private成员
Student::Student()
{
m_name = "";
m_math = 0;
m_Chinese = 0;
m_English = 0;
m_sum = 0;
}
Student::Student(std::string name, int math, int English, int Chinese)
{
m_name = name;
set_scores(math,English,Chinese);
}
void Student::set_scores(int math, int English, int Chinese)
{
m_math = math;
m_English = English;
m_Chinese = Chinese;
set_sum(); // 调用私有方法
}
double Student::get_average() const
{
return (double)m_sum / 3;
}
void Student::show() const
{
std::cout << "name: " << m_name << std::endl;
std::cout << "math: " << m_math << std::endl;
std::cout << "English: " << m_English << std::endl;
std::cout << "Chinese: " << m_Chinese << std::endl;
std::cout << "Sum: " << m_sum << std::endl;
}
Student::~Student()
{
std::cout << "This is destructor!" << std::endl;
}
内联方法
定义位于类声明中的函数都将自动成为内联函数,所以set_sum()是一个内联函数。
如果想在类声明外定义成员函数为内联函数,只要在函数定义时使用inline限定符即可。
class Student
{
private:
...
void set_sum();
...
};
inline void Student::set_sum()
{
m_sum = m_math + m_English + m_Chinese;
}
类的构造函数和析构函数
构造函数专门用于构造新对象、将值赋给他们的数据成员(做初始化工作)。
默认构造函数
类设计时如果没有提供任何构造函数,C++会自动提供一个默认构造函数。它是默认构造函数的隐式版本,不做任何工作。
类似于int a
创建了a但没有赋值。
Student::Student()
{
}
注意
只有当没有定义任何构造函数时,编译器才会提供默认构造函数。如果提供了构造函数,但是没有提供默认构造函数,则下面声明会报错
Student stu1; // 没有默认构造函数
可以自己提供一个默认构造函数,给成员变量赋初值
Student::Student()
{
m_name = "";
m_math = 0;
m_Chinese = 0;
m_English = 0;
m_sum = 0;
}
析构函数
用构造函数创建对象,对象过期时,程序会自动调用析构函数,用来完成清理工作。(做清理工作)
析构函数的名称是在类名前加上~
Student::~Student()
{
std::cout << "This is destructor!" << std::endl;
}
如果程序员没有提供析构函数,C++会隐式地声明一个默认析构函数。
Student::~Student()
{
}
如果使用了new创建成员变量,必须在析构函数中使用delete释放掉该成员变量。
Student~Student()
{
delete var;
}
析构函数何时被调用
- 如果创建的是静态存储类对象,则其析构函数将在程序结束时自动被调用。
- 如果创建的是自动存储类对象,则其析构函数将在执行完代码块时自动被调用。
- 如果对象是通过new创建的,当使用delete来释放内存时,其析构函数将自动被调用。
const成员函数
为了确保调用成员函数时,调用对象不被修改,可以将成员函数声明为const成员函数,在声明时,在函数后面加上const关键字。
// 声明
void show() const;
// 定义
void Student::show() const
{
...
}
使用类
使用对象的成员函数,和使用结构体成员一样,通过成员运算符.
或->
。
int main()
{
Student stu1; // 默认构造函数
stu1.show(); // 调用公有成员函数
Student stu2("zhangsan");
stu2.set_scores(90,98,88);
std::cout << stu2.get_average() << std::endl;
stu2.show();
Student stu3("lisi", 89,85,94);
stu3.show();
Student *stu4 = new Student;
stu4->show();
Student *stu5 = new Student("wangwu", 90,90,90);
stu5->show();
delete stu4;
delete stu5;
}
this指针
每一个类对象中都有一个this指针指向本对象。比如在给成员变量赋值时使用this指针
Student::Student(std::string name, int math, int English, int Chinese)
{
this->m_name = name;
this->set_scores(math,English,Chinese);
}
使用this指针实例
比较两个学生的平均数,并返回高分数的学生
class Student
{
...
public:
const Student &topval(const Student &) const;
...
}
const Student & Student::topval(const Student &stu) const
{
if(this->get_average() > stu.get_average())
{
return *this; // 返回本对象
}
else
{
return stu;
}
}
int main()
{
Student stu2("zhangsan");
stu2.set_scores(90,98,88);
std::cout << stu2.get_average() << std::endl;
stu2.show();
Student stu3("lisi", 89,85,94);
stu3.show();
Student stu6 = stu2.topval(stu3); // 比较stu2和stu3的成绩
stu6.show();
}
整体代码
#include <iostream>
#include <string>
using namespace std;
class Student // 关键字class标识类定义
{
// 默认是私有的
int m_math; // 私有成员
private: // 私有成员
std::string m_name; // 成员变量
int m_English;
int m_Chinese;
int m_sum;
void set_sum() // 内联方法
{
m_sum = m_math + m_English + m_Chinese;
}
public: // 公有成员
Student(); // 默认构造函数
Student(std::string name) // 构造函数
{
m_name = name;
m_English = 0;
m_Chinese = 0;
m_math = 0;
set_sum();
}
Student(std::string name, int math, int English, int Chinese); // 构造函数
void set_scores(int math, int English, int Chinese); // 公有成员函数
double get_average() const;
void show() const;
const Student &topval(const Student &) const;
~Student(); // 析构函数
};
int main()
{
Student stu1; // 默认构造函数
stu1.show(); // 调用公有成员函数
Student stu2("zhangsan");
stu2.set_scores(90,98,88);
std::cout << stu2.get_average() << std::endl;
stu2.show();
Student stu3("lisi", 89,85,94);
stu3.show();
Student stu6 = stu2.topval(stu3);
cout << "higher scores" << endl;
stu6.show();
cout << endl;
Student *stu4 = new Student;
stu4->show();
Student *stu5 = new Student("wangwu", 90,90,90);
stu5->show();
delete stu4;
delete stu5;
}
const Student & Student::topval(const Student &stu) const
{
if(this->get_average() > stu.get_average())
{
return *this;
}
else
{
return stu;
}
}
Student::Student()
{
m_name = "";
m_math = 0;
m_Chinese = 0;
m_English = 0;
m_sum = 0;
}
Student::Student(std::string name, int math, int English, int Chinese)
{
this->m_name = name;
this->set_scores(math,English,Chinese);
}
void Student::set_scores(int math, int English, int Chinese)
{
m_math = math;
m_English = English;
m_Chinese = Chinese;
set_sum(); // 调用私有方法
}
double Student::get_average() const
{
return (double)m_sum / 3;
}
void Student::show() const
{
std::cout << "name: " << m_name << std::endl;
std::cout << "math: " << m_math << std::endl;
std::cout << "English: " << m_English << std::endl;
std::cout << "Chinese: " << m_Chinese << std::endl;
std::cout << "Sum: " << m_sum << std::endl;
}
Student::~Student()
{
std::cout << "This is destructor!" << std::endl;
}
作用域为类的常量
在类中定义常量,并使用这个常量定义一个数组
错误的写法
class Person
{
private:
const int size = 30;
char m_name[size]; // 错误
};
这样会报错,因为声明类只是描述了对象的形式,并没有创建对象。因此,在创建对象前,将没有用于存储值的空间。
可行的方法
- 方法一,在类中声明一个枚举
在类声明中声明的枚举的作用域为整个类,因此可以用枚举为整型常量提供作用域为整个类的符号名称。
class Person
{
private:
enum {size = 30;};
char m_name[size];
};
用这种方式声明枚举并不会创建类数据成员。也就是说,所有对象中都不包括枚举。size只是一个符号枚举,在作用域为整个类的代码中遇到它时,编译器将用30来替换它。类似于宏定义。
- 方法二,使用关键字static
class Person
{
private:
static const int size = 30;
cahr m_name[size];
};
这样声明的常量将与其他静态变量存储在一起,而不是存储在对象中。
实例
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
private:
static const int size = 30;
// enum { size = 30 };
char m_name[size];
public:
Person();
Person(const char *name);
friend ostream &operator<<(ostream &os, const Person& p);
};
int main()
{
Person p1("zhangsan");
cout << p1 << endl;
return 0;
}
Person::Person()
{
m_name[0] = '\0';
}
Person::Person(const char *name)
{
strncpy(m_name, name, size - 1);
}
ostream &operator<<(ostream &os, const Person& p)
{
os << p.m_name;
return os;
}