类与对象
类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量;创建对象的过程也叫类的实例化。
将类的成员函数称为类的方法(Method)。
在面向对象的编程语言中,经常把函数(Function)称为方法(Method)。
类的定义
class
是 C++ 中新增的关键字,专门用来定义类
class 类名
{
public:
// 成员变量
// 成员函数
};
最后的;
很重要,注意检查,防止没必要的错误
public
也是 C++ 的新增关键字,它只能用在类的定义中,表示类的成员变量或成员函数具有“公开”的访问权限
创建对象
创建类:
class Student
{
public:
string name;
int age;
void say()
{
cout << name << "说:我今年"
<< age << "岁了!" << endl;
}
};
创建对象
class 类名 对象名;
或
类名 对象名;
例如:
class Student LiMing;
Student LiMing;
在创建对象时,class 关键字可要可不要,Student是类名,LiMing是对象名
除了创建单个对象,还可以创建对象数组
Student AllStu[100]; // 这就创建类容量为100的对象数组
访问类的成员
创建对象以后,可以使用点号.
来访问成员变量和成员函数
// 使用成员变量
LiMing.name = "李明";
LiMing.age = 25;
// 使用成员函数
LiMing.say();
使用对象指针
创建的对象 stu 在 栈上 分配内存,指针执行那个该对象,需要使用 取地址符 &
获取它的地址
Student stu;
Student *pStu = &stu;
pStu 是一个指针,它指向 Student 类型的数据,也就是通过 Student 创建出来的对象。
需要使用 new
关键字
类名* 对象名 = new 类名;
例如:
Student *pStu = new Student;
使用 new 在 堆上 创建出来的对象是匿名的,没法直接使用,必须要用一个指针指向它,再借助指针来访问它的成员变量或成员函数。
对象指针,可以通过箭头 ->
来访问对象的成员变量和成员函数
栈内存是程序自动管理的
堆内存由程序员管理,对象使用完毕后可以通过手动释放
new 和 delete 往往成对出现,以保证及时删除不再使用的对象,防止无用内存堆积。
Student* LiLei = new Student;
LiLei->name = "李雷";
LiLei->age = 63;
LiLei->say();
例子:
#include
#include
using namespace std;
class Student
{
public:
// 成员变量
string name;
int age;
// 成员函数
void say()
{
cout << name << "说:我今年"
<< age << "岁了!" << endl;
}
};
int main(int argc,char** argv)
{
// 创建实例对象
Student LiMing;
LiMing.name = "李明";
LiMing.age = 25;
LiMing.say();
// 使用指针创建实例对象
Student* LiLei = new Student;
LiLei->name = "李雷";
LiLei->age = 63;
LiLei->say();
delete LiLei;// 释放空间
return 0;
}
匿名对象
格式:
(new 类名) -> 成员函数或成员变量;
例如:
(new Student)->name = "小明";
(new Student)->age = 23;
每个匿名对象互不影响,互不关联,都是单独的个体
成员函数
类这种数据类型是一个包含成员变量和成员函数的集合。
成员函数和普通函数类似,都有返回值和参数列表,但作用域不同
成员函数可以的定义在类体内,也可以定义在类外
class Student
{
public:
// 成员函数,在类体内部声明
void say()
{
cout << "Hello World!" << endl;
}
};
在类体外声明,需要用到 域解析符(也称作用域运算符或作用域限定符) ::
格式:
class 类名
{
数据类型 函数名(参数列表);
}
(inline) 数据类型 类名::函数名(参数列表)
{
// 函数体
}
例如:
class Student
{
public:
void say();
};
void Student::say()
{
cout << "Hello World!" << endl;
}
inline 是可选的,可根据具体情况而定
在类体外定义 内联 (inline
) 函数的方式,必须将类的定义和成员函数的定义都放在同一个头文件中(或者同一个源文件中),否则编译时无法嵌入到类中
类成员的权限访问
成员函数和成员变量的访问权限修饰符:
- public
- private
- protected
(private)私有成员变量和成员函数只能在类内部使用,在类外都是无效的
成员函数和成员变量无论使用何种修饰符修饰,在类内部,均可以随意使用
在类外部,只能通过声明的实例对象访问访问 public 属性的成员,不能访问 private、protected 属性的成员
class Student
{
// 私有化,只能在类内部使用
private:
int age;
// 保护模式,只能在类及 子类(继承) 中使用
protected:
string name;
// 公共的,可以任意访问
public:
void say()
{
cout << name << "今年" << age << "岁!" << endl;
}
};
一般情况下,需要将类的成员变量隐藏(private),外部不能直接修改,需要使用特定的接口来set函数,使用 get函数来获取,(在英语中,可以知道 set-设置 ,get-获取, 语义化,更容易理解)
设置成员变量的函数的名字通常以set开头,后跟成员变量的名字,所以通常称为 set 函数。
读取成员变量的函数的名字通常以get开头,后跟成员变量的名字,所以通常称为 get 函数。
class Student
{
private:
int age;
public:
void setAge(int age)
{
age = age;
}
int getAge()
{
return age;
}
};
在setAge中,存在两个age,会有些分不清,会产生混淆,因此在定义私有变量命名时,使用特殊格式,以 m_
开头,例: m_age
,
class Student
{
private:
int m_age;
public:
void setAge(int age)
{
m_age = age;
}
int getAge()
{
return m_age;
}
};
如果不写任何修饰符,就会 默认为 private。
声明为 private 的成员和声明为 public 的成员的次序任意,既可以先出现 private 部分,也可以先出现 public 部分。
this指针
this 是 C++ 中的一个关键字,也是一个 const 指针,它指向当前对象,通过它可以访问当前对象的所有成员。
this 只能用在类的内部,通过 this 可以访问类的所有成员,包括 private、protected、public 属性的。
this 是一个指针,要用->来访问成员变量或成员函数
class Student
{
private:
int age;
public:
void setAge(int age)
{
this->age = age;
}
}
使用this,可以清晰的区分参数,有助于理解类的作用
this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。
this 作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。
#include
#include
using namespace std;
class Student
{
// 成员变量
private:
int age;
string name;
public:
// 成员函数
void setAge(int age)
{
// 设置age
this->age = age;
}
void setName(string name)
{
// 设置name
this->name = name;
}
int getAge()
{
// 获取age
return this->age;
}
string getName()
{
// 获取name
return this->name;
}
void say()
{
cout << this->name << "说:来,骗,来,偷袭," << this->age << "岁的老同志" << endl;
}
};
int main(int argc,char** argv)
{
Student Sir;
Sir.setName("马保国");
Sir.setAge(69);
Sir.say();
return 0;
}
结果:
马保国说:来,骗,来,偷袭,69岁的老同志
构造函数
构造函数 : 名字和类名相同,没有返回值,不需要用户显式调用(用户也不能调用),而是在创建对象时自动执行
构造函数 :在创建对象的同时为成员变量赋值
默认情况下,系统会默认给类添加一个构造函数
Student(){}
当已存在构造函数,系统就不会添加默认的构造函数
不管是声明还是定义,构造函数名前面都不能出现返回值类型,即使是 void 也不允许;
#include
#include
using namespace std;
class Student
{
private:
// 成员变量
int age;
string name;
public:
// 构造函数
Student(int age,string name){
this->age = age;
this->name = name;
}
// 成员函数
void say()
{
cout << this->name << "说:来,骗,来,偷袭," << this->age << "岁的老同志" << endl;
}
};
int main(int argc, char** argv)
{
Student Sir(69,"马保国");
Sir.say();
return 0;
}
结果:
马保国说:来,骗,来,偷袭,69岁的老同志
一个类可以存在多个构造函数
构造函数的调用是强制性的,一旦在类中定义了构造函数,那么创建对象时就一定要调用,不调用是错误的,防止出现错误,一般可以手动添加一个 无参 构造函数
创建对象时只有一个构造函数会被调用
#include
#include
using namespace std;
class Student
{
private:
int age;
string name;
public:
// 无参构造函数
Student() {
this->age = 69;
this->name = "老同志";
}
// 构造函数
Student(int age,string name){
this->age = age;
this->name = name;
}
// 成员函数
void say()
{
cout << this->name << "说:来,骗,来,偷袭," << this->age << "岁的老同志" << endl;
}
};
int main()
{
Student Sir;
Sir.say();
return 0;
}
结果:
老同志说:来,骗,来,偷袭,69岁的老同志
也可以使用 对象指针
Student* Sir = new Student();
Sir->say();
delete Sir;
Student* Man = new Student(69,"马保国");
Man->say();
delete Man;
构造函数也可以定义在 类体 外
class Student
{
private:
int age;
string name;
public:
Student(int age, string name);
};
Student::Student(int age, string name) {
this->age = age;
this->name = name;
}
构造函数初始化列表
格式 ( Student 类):
class Student
{
private:
int m_age;
string m_name;
public:
Student(int age, string name);
};
Student::Student(int age, string name):m_age(age),m_name(name) {}
定义构造函数时并没有在函数体中对成员变量一一赋值,其函数体为空(可以有其他语句)
而是在函数首部与函数体之间添加了一个冒号 :
,后面紧跟 m_age(age),m_name(name)
, 语句,这个语句的意思相当于函数体内部的 m_age = age;m_name = name;
语句
使用构造函数初始化列表并没有效率上的优势,仅仅是书写方便
成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关。
构造函数初始化列表还有一个很重要的作用 : 初始化 const 成员变量
class Student
{
private:
const int m_len = 0;
public:
Student(int len);
void say()
{
cout << this->m_len << endl;
}
};
Student::Student(int len): m_len(len){}
析构函数
创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作,例如释放分配的内存、关闭打开的文件等,这个函数就是 析构函数。
析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要程序员显式调用(程序员也没法显式调用),而是在销毁对象时自动执行。
- 构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个
~
符号。
- 析构函数没有参数,不能被重载,因此一个类只能有一个析构函数。
- 如果没有定义,编译器会自动生成一个默认的析构函数。
例如:
~Student(){}
用 new 分配内存时会调用构造函数,用 delete 释放内存时会调用析构函数。
class Student
{
private:
int m_len;
int* arr;
public:
Student(int len);
~Student();
};
Student::Student(int len):m_len(len){
// 在堆上创建数组
if( len > 0) this->arr = new int[len];
else this->arr = new int[10];
}
Student::~Student()
{
delete arr; // 释放数组内存
}
new 创建的 对象 位于 堆区 ,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行。
#include
using namespace std;
class Student
{
private:
int m_len;
public:
Student(int len);
~Student();
};
Student::Student(int len):m_len(len){}
Student::~Student()
{
cout << this->m_len << endl;
}
int main(int argc, char** argv)
{
Student* Man = new Student(1);
Student* Woman = new Student(2);
delete Man;
return 0;
}
输出结果为:
1
静态成员
静态成员变量
静态成员变量是一种特殊的成员变量,它被关键字 static
修饰
例如:
class Student
{
public:
static int count;
};
static 成员变量属于类,不属于某个具体的对象,即使创建多个对象,只会 分配一份内存,所有对象使用的都是这份内存中的数据。
静态成员变量,不能通过构造函数初始化,因为其不是类的非静态成员
静态成员变量初始化,需要在类外完成
访问静态成员变量的方式:
//通过类类访问 static 成员变量
Student::count = 10;
//通过对象来访问 static 成员变量
Student stu;
stu.count = 20;
//通过对象指针来访问 static 成员变量
Student *pstu = new Student;
pstu -> count = 20;
static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问。
在类内部可以访问静态成员变量,也可以使用this指针
#include
#include
using namespace std;
class Student
{
private:
string m_name;
public:
static int count;
public:
Student(string name):m_name(name){
count++;
};
void say()
{
// 类内部访问 静态成员变量
cout << "第 " << this->count << " 位同学: " << this->m_name << endl;
}
};
// 初始化静态成员变量
int Student::count = 0;
int main()
{
Student s1("小明");
cout << s1.count << endl;
s1.say();
Student* s2 = new Student("李雷");
cout << s2->count << endl;
s2->say();
delete s2;
cout << "全班有" << Student::count << "位同学" << endl;
return 0;
}
结果:
1
第 1 位同学: 小明
2
第 2 位同学: 李雷
全班有2位同学
- 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量
- static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。
static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。
- 静态成员变量必须初始化,而且只能在类体外进行
- 静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。当通过对象名访问时,对于不同的对象,访问的是同一份内存。
静态成员函数
静态成员函数只能访问静态成员
静态成员函数没有 this 指针,函数内部不能使用this指针
class Student
{
public:
static int count;
public:
static void say()
{
cout << count << endl;
// cout << this->count << endl; 错误的格式
}
void run()
{
this->say(); // 可以访问静态成员函数
}
};
静态成员函数在声明时要加 static,在定义时不能加 static
class Student
{
public:
static int count;
public:
static void say();
void run();
};
void Student::say()
{
cout << count << endl;
}
void Student::run()
{
this->say();
}
const成员
const 成员变量,只需在定义时,使用 const
关键字,通过 构造函数 进行初始化
const 成员函数 :称为常成员函数。
const 成员函数可以使用类中的所有成员变量,但是不能修改它们的值
- 常成员函数 需要在声明和定义的时候在函数头部的 结尾 加上 const 关键字
- 函数 开头 的 const 用来修饰 函数的返回值
- 必须在成员函数的声明和定义处同时加上 const 关键字
int getScore() const;
int Student::getScore() const
{
return m_score;
}
- 通常将 get 函数设置为常成员函数
#include
#include
using namespace std;
class Student
{
private:
string m_name;
int m_score;
public:
// 构造函数
Student(string name,int m_score);
// 类体内声明
string getName() const
{
// this->m_age = "小明"; 显示错误,不能使用this指针
return m_name;
};
// 类体外声明
int getScore() const;
};
int Student::getScore() const
{
return m_score;
}
// 构造函数
Student::Student(string name,int score) : m_name(name),m_score(score) {};
int main()
{
Student s("李雷",75);
cout << s.getName() << endl;
cout << s.getScore() << endl;
return 0;
}
const 对象
const用来修饰对象,称为常对象
一旦将对象定义为 常对象 之后,就 只能调用类的 const 成员(包括 const 成员变量和 const 成员函数)
const 类名 对象名;
例如:
const Student Sir;
可以定义 const 指针
const Student* Sir = new Student;
例如:
#include
#include
using namespace std;
class Student
{
private:
string m_name;
int m_score;
public:
// 构造函数
Student(string name, int score) : m_name(name), m_score(score) {};
int getScore() const
{
return m_score;
}
void show()
{
cout << m_name << "的英语" << m_score << endl;
}
string getName() const
{
//show(); 错误,不能使用this指针,也不能访问
cout << "英语" << getScore() << "的";
return m_name;
};
};
int main()
{
const Student s("李雷", 75);
cout << s.getName() << endl;
//s.show(); 错误
const Student * sir = new Student("小强",100);
cout << sir->getName() << endl;
sir->show(); 错误
return 0;
}
友元函数
借助友元(friend),可以使得 其他类 中的 成员函数 以及全局范围内的函数 访问 当前类的 private 成员。
使用关键字 friend
, 借助 友元 可以访问与其有好友关系的类中的私有成员
注意,友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。
参数 传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。
将非成员函数声明为友元函数
注意:
- 友元函数的定义,一定要在类的定义之后,否则无法访问
#include
#include
using namespace std;
class Student
{
private:
string m_name;
string m_work;
public:
// 构造函数
Student(string name, string work) :m_name(name), m_work(work) {};
public:
// 友元函数
friend void show(Student* s);
friend void say(Student s);
};
void show(Student* s)
{
cout << "朋友们好啊, 我是" << s->m_work << s->m_name << endl;
}
void say(Student s)
{
cout << "大家好, 我是" << s.m_work << s.m_name << endl;
}
int main()
{
Student mbg("马保国","浑元形意太极门掌门人");
show(&mbg);
Student cxk("坤坤","练习两年半的偶像练习生");
say(cxk);
return 0;
}
结果:
朋友们好啊, 我是浑元形意太极门掌门人马保国
大家好, 我是练习两年半的偶像练习生坤坤
将其他类的成员函数声明为友元函数
friend类 表示 拥有友元函数(friend)的类
member类 表示 拥有成员函数的类
类之间的友元函数,需要注意类的顺序,以及成员函数定义的位置和方式:
- member类 要先定义,之后再定义 friend类
下例: 由于 类 Introduct
中的成员函数要 作为 类 Student
的友元函数,因此可以访问类 Student
的成员 ,所以 类 Introduct
需要先定义
- 类必须在正式声明之后才能使用;但是某些情况下,只要做好提前声明,也可以先使用。由于 friend类,在 member类 之后定义,当执行到 member类 时,需要用到friend类 ,但是 friend类 没有定义, 因此需要 提前声明
下例:
class Student; // 很有必要的提前声明
- member类 的成员函数
声明
和实现
要分开,需要将实现
方式放在 friend类 的定义之后
- friend类 对应的就是 出现
friend
的类,共享自己的数据
定义的一般顺序:
第一步:
class friend;
第二步:
class member{
// 成员变量 , 成员函数
// 需要 友元的成员函数 声明
}
第三步:
class friend{
// 成员变量 , 成员函数
// friend member类的 友元成员函数
}
第四步:
member类的 友元成员函数 的实现
#include
#include
using namespace std;
class Student; // 很有必要的提前声明
// 第一个类, member类
class Introduct
{
private:
static int count;
public:
// 构造函数
Introduct() { count++; };
public:
// 成员函数
void show(Student* s);
void say(Student s);
};
// 第二个类, friend类
class Student
{
private:
string m_name;
string m_work;
public:
// 构造函数
Student(string name, string work) :m_name(name), m_work(work) {};
public:
// 友元函数
friend void Introduct::show(Student* s);
friend void Introduct::say(Student s);
};
// 第一个类的成员函数,即第二个类的友元函数
void Introduct::show(Student* s)
{
cout << this->count << ". 朋友们好啊, 我是" << s->m_work << s->m_name << endl;
}
void Introduct::say(Student s)
{
cout << this->count << ". 大家好, 我是" << s.m_work << s.m_name << endl;
}
int Introduct::count = 0;
int main()
{
Student mbg("马保国","浑元形意太极门掌门人");
(new Introduct)->show(&mbg); // 匿名对象
Student cxk("坤坤","练习两年半的偶像练习生");
(new Introduct)-> say(cxk); // 匿名对象
}
结果:
1.朋友们好啊, 我是浑元形意太极门掌门人马保国
2.大家好, 我是练习两年半的偶像练习生坤坤
友元类
将类 B 声明为类 A 的友元类,那么类 B 中的所有成员函数都是类 A 的友元函数,可以访问类 A 的所有成员,包括 public、protected、private 属性的。
除非有必要,一般不建议把整个类声明为友元类,而只将某些成员函数声明为友元函数
friend类 表示 拥有友元函数(friend)的类
member类 表示 拥有成员函数的类
注意:
- 优先定义 friend类,其次是 member类
- friend类 对应的就是 出现
friend
的类,共享自己的数据
#include
#include
using namespace std;
// 第一个类, friend类
class Introduct
{
private:
static int count;
public:
// 构造函数
Introduct() { count++; };
public:
// 友元类
friend class Student;
};
// 第二个类, member类
class Student
{
private:
string m_name;
string m_work;
public:
// 构造函数
Student(string name, string work) :m_name(name), m_work(work) {};
public:
// 友元函数
void show(Introduct* s); // 类体外
void say(Introduct s) // 类体内
{
cout << s.count << ". 大家好, 我是" << this->m_work << this->m_name << endl;
};
};
void Student::show(Introduct* s)
{
cout << s->count << ". 朋友们好啊, 我是" << this->m_work << this->m_name << endl;
}
// 静态成员变量
int Introduct::count = 0;
int main()
{
Student mbg("马保国","浑元形意太极门掌门人");
Introduct mg;
mbg.show(&mg);
Student cxk("坤坤","练习两年半的偶像练习生");
Introduct kk;
cxk.say(kk);
return 0;
}
结果:
1.朋友们好啊, 我是浑元形意太极门掌门人马保国
2.大家好, 我是练习两年半的偶像练习生坤坤
友元的关系不能传递。如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类, 类C 不能使用 类 A 的成员
友元的关系是单向的而不是双向的。如果声明了类 B 是类 A 的友元类,不等于类 A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。
class与struct
struct 是 C语言 的关键字
在C语言中,struct 只能包含成员变量,不能包含成员函数。而在C++中,struct 类似于 class,既可以包含成员变量,又可以包含成员函数。
struct 和 class 基本是通用的,仍存在一些区别:
- 使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。
- class 继承默认是 private 继承,而 struct 继承默认是 public 继承
- class 可以使用模板,而 struct 不能
例如:
#include
#include
using namespace std;
// 使用 "struct" 结构体
struct Student
{
int number = 6;
static int count;
private:
// 成员变量
string m_name;
string m_work;
public:
// 构造函数
Student(string name, string work) :m_name(name), m_work(work) {
// 计数
count++;
};
// 析构函数
~Student() { cout << this->m_name <count << ". 大家好, 我是" << this->m_work << this->m_name << endl;
}
};
void Student::show()
{
cout << this->count << ". 朋友们好啊, 我是" << this->m_work << this->m_name << endl;
}
// 静态成员变量初始化
int Student::count = 0;
int main()
{
Student mbg("马保国","浑元形意太极门掌门人");
mbg.show();
Student cxk("坤坤","练习两年半的偶像练习生");
cxk.say();
// 默认为 public ,因此可以访问number
cout << cxk.number << endl;
return 0;
}
结构:
1. 朋友们好啊, 我是浑元形意太极门掌门人马保国
2. 大家好, 我是练习两年半的偶像练习生坤坤
6
坤坤
马保国