C++学习笔记 | 面向对象编程-对象和类
前言
类是C++中特有的,在C中没有类这个概念。C++ 中的类(Class)可以看做C语言中结构体(Struct)的升级版,稍微修改一下定义语句即可class,public:。
在C中结构体中不能定义函数,类中可以定义函数。(C++中结构体也可以定义函数)
结构体和类都可以看做一种由用户自己定义的复杂数据类型,在C语言中可以通过结构体名来定义变量,在 C++ 中可以通过类名来定义变量。不同的是,通过结构体定义出来的变量还是叫变量,而通过类定义出来的变量有了新的名称,叫做对象(Object)
在C语言中,我们会把重复使用或具有某项功能的代码封装成一个函数,将拥有相关功能的多个函数放在一个源文件,再提供一个对应的头文件,这就是一个模块。使用模块时,引入对应的头文件就可以。而在 C++ 中,多了一层封装,就是类(Class)。类由一组相关联的函数、变量组成,你可以将一个类或多个类放在一个源文件,使用时引入对应的类就可以。
类可以理解为一种新的数据类型,该数据类型的名称是 Student。与 char、int、float 等基本数据类型不同的是,Student 是一种复杂数据类型。
从结构体到类
#include <iostream> //包含头文件
using namespace std; //指定缺省的命名空间
//结构体
struct st_girl //超女基本信息结构体st_girl,存放了超女全部的数据项
{
string name; //姓名
int age; //年龄
};
int main()
{
st_girl girl; //声明结构体变量
girl.name = "西施";
girl.age = 26;
cout << "超女的姓名:" << girl.name << endl;
cout << "超女的年龄:" << girl.age << endl;
}
在实际开发中main函数应该只负责主要业务逻辑,代码不能太啰嗦。修改代码
#include <iostream> //包含头文件
using namespace std; //指定缺省的命名空间
//传统写法
struct st_girl //超女基本信息结构体st_girl,存放了超女全部的数据项
{
string name; //姓名
int age; //年龄
};
void setvalue(st_girl& girl, string name, int age)
{
girl.name = name;
girl.age = age;
}
void show(const st_girl& girl)
{
cout << "超女的姓名:" << girl.name << endl;
cout << "超女的年龄:" << girl.age << endl;
}
int main()
{
st_girl girl; //声明结构体变量
setvalue(girl, "西施", 26);
show(girl);
}
用类来写:
#include <iostream> //包含头文件
using namespace std; //指定缺省的命名空间
//传统写法
class Cgirl //超女基本信息结构体st_girl,存放了超女全部的数据项
{
public:
string name; //姓名
int age; //年龄
void setvalue(string name1, int age1)
{
name = name1;
age = age1;
}
void show()
{
cout << "超女的姓名:" << name << endl;
cout << "超女的年龄:" << age << endl;
}
};
int main()
{
Cgirl girl; //声明结构体变量
girl.setvalue("西施", 26);
girl.show();
}
类的定义语法
class 类的名称
{
public:
成员一的数据类型 成员一名;
成员二的数据类型 成员二名;
...
}
封装是指把琐碎的代码打包起来,为调用者程序提供方便
类的注意事项
- 类的成员可以是变量,也可以是函数
- 类的成员函数可以定义在类的外面
class Cgirl //超女基本信息结构体st_girl,存放了超女全部的数据项
{
public:
string name; //姓名
int age; //年龄
void setvalue(string name1, int age1);
};
void Cgirl::setvalue(string name1, int age1)
{
name = name1;
age = age1;
}
- 用类定义一个类的变量叫做创建一个对象
- 类的成员变量和成员函数的作用域和生命周期与对象的作用域和生命周期相同
类的访问权限
类的成员有三种访问权限:public、private和protected,分别表示公有的、私有的和受保护的。
在类的内部,无论成员被声明为public还是private,都是可以访问的。
在类的外部,只能访问public成员,不能访问private、protected成员。
在一个类的定义中,private和public可以出现多次。
class Cgirl //超女基本信息结构体st_girl,存放了超女全部的数据项
{
public://从这里的后面全部成员的权限都是公有的
string name; //姓名
private://从这里的后面全部成员的权限都是私有的
int age; //年龄
public://从这里的后面全部成员的权限又都是公有的
void setvalue(string name1, int age1);
};
//姓名和setvalue函数公有,年龄私有
void Cgirl::setvalue(string name1, int age1)
{
name = name1;
age = age1;
}
结构体的成员缺省为public,类的成员缺省为private。
简单使用类
要用好类关键在于编程思想和方法的改变,否则会成为披着C++外衣的C程序员
- 类的成员函数可以直接访问该类其它的成员函数(可以递归)
- 类指针的用法与结构体指针的用法相同
- 类的成员可以是任意数据类型(类中枚举)
- 可以为类的成员指定缺省值(C++11标准)
- 在类的外部,一般不直接访问(读和写)对象的成员,而是用成员函数。
- 对象一般不用memset()清空成员变量,可以写一个专门用于清空成员变量的成员函数。
- 对类和对象用sizeof()运算符意义不大,一般不用。
- 用结构体描述纯粹的数据,用类描述对象。
- 在类的声明中定义的函数都将自动成为内联函数;在类的声明之外定义的函数如果使用了inline限定符,也是内联函数。
构造函数和析构函数
**构造函数:**在创建对象时,自动的进行初始化工作。
**析构函数:**在销毁对象前,自动的完成清理工作。
初始化和清理工作都极其重要
构造函数和析构函数是类中特别的成员函数。
构造函数
- 访问权限必须是public
- 函数名必须与类名相同
- 没有返回值,不写void
- 可以有参数,可以重载,可以有默认参数
- 创建对象时会自动调用一次,不能手工调用
语法:
类名(){....}
例子:
class CGirl //定义超女类CGirl
{
public:
string m_name;
int m_age;
char m_memo[301];
CGirl()
{
m_name.clear();m_age=0;memset(m_memo,0,sizeof(m_memo));
cout<<"调用了CGirl()构造函数\n";
}
析构函数
- 访问权限必须是public
- 函数名必须在类名前加
- 没有返回值,也不写void
- 没有参数,不能重载
- 销毁对象前只会自动调用一次,但是可以手工调用
语法:
~类名(){...}
构造函数的细节
留个坑,以后再填
拷贝构造函数
用一个已存在的对象创建新的对象,不会调用(普通)构造函数,而是调用拷贝构造函数。
如果类中没有定义拷贝构造函数,编译器将提供一个拷贝构造函数,它的功能是把已存在对象的成员变量赋值给新对象的成员变量。
它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。
用一个已存在的对象创建新的对象的语法:
类名 新对象名(已存在的对象名);
类名 新对象名=已存在的对象名;
拷贝构造函数的语法:
类名(const 类名&对象名){...}
class CGirl
{
public:
string m_name;
int m_age;
//没有参数的普通构造函数
CGirl(){m_name.clesr();m_age=0;cout<<"调用了CGirl构造函数。\n";}
//没有重载的拷贝构造函数(默认拷贝构造函数)
CGirl(const CGirl&g){m_name.clesr();m_age=0;cout<<"调用了CGirl构造函数。\n";}
//析构函数
~CGirl(){cout<<"调用了~CGirl()\n";}
}
//在拷贝构造函数中,什么代码都可以写,不过,一般只会写变量初始化和拷贝相关的代码
注意:
访问权限必须是public
函数名必须与类名相同
没有返回值,不写void
如果类中定义了拷贝构造函数,编译器将不再提供默认的拷贝构造函数(编译器提供的构造函数是空实现,但是拷贝构造函数是干活的)
拷贝构造函数使用场景:
- 用一个已存在的对象创建新的对象,不会调用(普通)构造函数,而是调用拷贝构造函数。
- 以值传递的方式调用函数时,如果实参为对象,会调用构造拷贝函数。
- 函数以值的方式返回对象时,可能会调用拷贝构造函数(VS会调用,Linux不会)
拷贝构造函数可以重载,可以有默认参数
浅拷贝和深拷贝
浅拷贝
深拷贝
初始化列表
构造函数的执行可以分成两个阶段:初始化阶段和计算阶段。初始化阶段先于计算阶段。
- 初始化阶段:全部的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中。
- 计算阶段:一般是指用于执行构造函数体内的赋值操作。
构造函数除了参数列表和函数体之外,还可以有初始化列表。
初始化列表的语法:
类名(形参列表):成员一(值一),成员二(值二),...,成员n(值n){...}//相当于用初始化列表赋值
注意:
- 如果成员已经在初始化列表中,则不应该在构造函数中再次赋值
- 初始化列表的括号中可以是具体的值,也可以是构造函数的形参名,还可以是表达式
CGirl(string name,int age):m_name(name),m_age(age)//构造函数的形参名
- 初始化列表与赋值有本质区别,如果成员是类,使用初始化列表调用的是拷贝构造函数,而赋值则是先创建对象(调用普通构造函数),然后再赋值。
const修饰成员函数
在类的成员函数后面加const关键字,表示在成员函数中不会调用对象的成员变量。
this指针
如果类的成员函数中涉及多个对象,在这种情况下需要使用this指针。