面向对象的特点:封装、继承、多态。万事万物皆为对象,对象上有其属性和行为(方法)。
1、封装C++封装将属性与行为作为一个整体,表现生活中的事物
将属性和行为加以权限控制(public、private等)
语法:
class 类名{
访问权限:
属性(变量)列表
行为(方法)列表
}
#includeusing namespace std; #define PI 3.14 //类 - 圆 class Circle { //访问权限 public: //属性 //半径 int radius; //方法 //计算周长 double circumference() { return 2 * PI * radius; } //计算面积 double area() { return (PI * radius) * (PI * radius); } }; int main() { //类和对象 //创建对象:通过一个类创建一个对象的过程,即实例化 Circle c1; //给对象属性赋值 c1.radius = 2; //调用对象方法 cout << "半径为 " << c1.radius << " 时,圆周长 = " << c1.circumference() << ", 圆面积 = " << c1.area() << endl; system("pause"); return 0; }
输出结果
Go语言封装半径为 2 时,圆周长 = 12.56, 圆面积 = 39.4384
Go语言中没有class关键字,即没有类这个概念,可以通过函数方法表达类的概念,通过函数方法指定某个结构体来实现类。
语法:
func (变量名 结构体名)函数方法名(参数列表) 返回值列表 {
函数体
}
//结构体名可以是结构体值类型,也可以是结构体指针类型
package main
import "fmt"
const PI = 3.14
//结构体 - 圆
type Circle struct {
//半径
radius int
}
//结构体方法
//计算周长
func (c *Circle) circumference() float64 {
return 2 * PI * float64(c.radius)
}
//计算面积
func (c *Circle) area() float64 {
return (PI * float64(c.radius)) * (PI * float64(c.radius))
}
func main() {
//创建结构体对象,同时给属性赋值
c1 := Circle{
radius: 2,
}
//调用对象方法
fmt.Printf("半径为 %d 时,圆周长 = %f, 圆面积 = %fn", c1.radius, c1.circumference(), c1.area())
}
输出结果
2、访问权限 C++访问权限半径为 2 时,圆周长 = 12.560000, 圆面积 = 39.438400
C++访问权限有三种
- public 公共权限 成员 类内可以访问,类外也可以访问
- protected 保护权限 成员 类内可以访问,类外不可以访问,继承时,子类可以访问父类成员
- private 私有权限 成员 类内可以访问,类外不可以访问,继承时,子类不可以访问父类成员
#includeusing namespace std; #define PI 3.14 //类 - 人 class Person { public: //姓名 string name; protected: //手机号 string phone; private: //账号密码 string password; public: void setPerson() { name = "Tracy"; phone = "13600000000"; password = "123"; } }; int main() { //类 - 封装 - 访问权限 Person p1; //调用对象公共访问方法 p1.setPerson(); cout << "对象访问权限:能够访问public权限:姓名:" << p1.name << endl; cout << "对象访问权限:不能访问protected权限:手机号" << endl; cout << "对象访问权限:不能访问private权限:密码" << endl; system("pause"); return 0; }
输出结果
对象访问权限:能够访问public权限:姓名:TracyGo语言权限访问
对象访问权限:不能访问protected权限:手机号
对象访问权限:不能访问private权限:密码
Go语言里访问权限也没有public、protected、private关键字,访问权限如下:
- 同包下不同文件的全局资源可以随意访问
- 不同包下资源,首字母大写时可以被不同包访问(可以当作public),首字母小写则不能补不同包访问(可以当作protected)
Go语言结构体对象可以访问自己的所有成员。
3、C++ struct与class区别struct 成员默认权限是public,class 成员默认权限是private
#includeusing namespace std; #define PI 3.14 //类 class C { int id; //默认权限为private }; //结构体 struct S { int id; //默认权限为public }; int main() { //类 - 封装 - struct与class区别:struct 成员默认权限是public,class 成员默认权限是private //访问类成员 C c1; //c1.id = 5; //报错:不可访问 cout << "访问类成员 c1.id = 5时,报错:不可访问" << endl; //访问结构体成员 S s1; s1.id = 5; //正常访问 cout << "访问结构体成员,正常访问, s1.id = " << s1.id << endl; system("pause"); return 0; }
输出结果
4、C++成员属性私有性访问类成员 c1.id = 5时,报错:不可访问
访问结构体成员,正常访问, s1.id = 5
- 将所有成员属性设置为私有,可以自己控制读写权限
- 对于写权限,可以检查数据的有效性
#includeusing namespace std; #define PI 3.14 //类 - 人 class Person { private: //以下3个成员属性都设置为私有属性 //姓名 string name; //手机号 string phone; //账号密码 string password; public: //成员属性姓名:设置可读可写 string getName() { return name; } void setName(string _name) { name = _name; } //成员属性手机号:设置只读 string getPhone() { return phone; } //成员属性账号密码:设置只写,同时检查设置的密码是否大于等于6位 void setPassword(string _pwd) { if (_pwd.length() < 6) { cout << "账号密码长度大于等于6位" << endl; return; } password = _pwd; } }; int main() { //类 - 封装 - 成员属性私有性 Person p1; //调用成员属性姓名:设置可读可写 p1.setName("Tracy"); cout << "成员属性姓名:" << p1.getName() << endl; //调用成员属性手机号:设置只读 cout << "成员属性手机号:" << p1.getPhone() << endl; //调用属性账号密码:设置只写,同时检查设置的密码是否大于等于6位 p1.setPassword("123"); system("pause"); return 0; }
输出结果
5、C++构造函数与析构函数 5.1、构造函数与析构函数成员属性姓名:Tracy
成员属性手机号:
账号密码长度大于等于6位
一个对象或变量没有初始状态,对其使用后果是未知的,同样的,使用完一个对象或变量,没有及时清理,也会造成一定的安全问题。
C++利用构造函数和析构函数解决以上问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制要我 们做的事情,因此如果不提供构造函数和析构函数,编译器会提供构造函数和析构函数的空实现。
- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数语法:类名( 参数列表 ) { 函数体 }
- 没有返回值,也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以有重载
- 程序在调用对象时会自动调用构造函数,无须手动调用,而且只会调用一次
析构函数语法:~类名() { 函数体 }
- 没有返回值,也不写void
- 函数名称与类名相同,且在函数名称前加上符号~
- 没有参数,因此不可能发生重载
- 程序在对象销毁前会自动调用析构函数,无须手动调用,而且只会调用一次
#includeusing namespace std; #define PI 3.14 //类 - 圆 class Circle { public: //半径 int radius; //构造函数 Circle() { cout << "Circle 构造函数调用" << endl; } //析构函数 ~Circle() { cout << "~Circle 析构函数调用" << endl; } //计算周长 double circumference() { return 2 * PI * radius; } //计算面积 double area() { return (PI * radius) * (PI * radius); } }; void f1() { Circle c1; //创建对象时,调用构造函数 c1.radius = 2; cout << "半径为 " << c1.radius << " 时,圆周长 = " << c1.circumference() << ", 圆面积 = " << c1.area() << endl; //程序在对象销毁前会自动调用析构函数 } int main() { //类和对象 - 构造函数与析构函数 f1(); system("pause"); return 0; //程序 }
输出结果
5.2、构造函数分类及调用Circle 构造函数调用
半径为 2 时,圆周长 = 12.56, 圆面积 = 39.4384
~Circle 析构函数调用
构造函数分类:
- 按照参数分类:无参构造函数(默认)和有参构造函数
- 按照类型分类:普通构造函数 和 拷贝构造函数
构造函数调用:
- 括号法
- 显示法
- 隐式转换法
构造函数调用 注意:
- 调用默认构造函数时,不要加()
Circle c1(); //此处编译器会认为是一个函数声明,而不是创建对象,若不实现函数声明,编译时会报错
- 不要使用拷贝构造函数初始化匿名对象
Circle(c6); //此处编译器会认为 Circle c6; 即创建一个对象 c6,但c6对象已存在,所以编译时会报错
#includeusing namespace std; #define PI 3.14 //类 - 圆 class Circle { public: //半径 int radius; //构造函数 - 无参 Circle() { cout << "Circle 无参构造函数调用" << endl; } //构造函数 - 有参 Circle(int _radius) { radius = _radius; cout << "Circle 有参构造函数调用,参数 _radius = " << _radius << endl; } //拷贝构造函数 Circle(const Circle& _circle) { //将参数拷贝给自己成员 radius = _circle.radius; cout << "Circle 拷贝构造函数调用,常量引用参数拷贝结果 radius = " << radius << endl; } //析构函数 ~Circle() { cout << "~Circle 析构函数调用" << endl; } //计算周长 double circumference() { return 2 * PI * radius; } //计算面积 double area() { return (PI * radius) * (PI * radius); } }; void f1() { //构造函数调用 //1、括号法 Circle c1; //调用默认构造函数,即无参的构造函数 Circle c2(3); //调用有参构造函数 Circle c3(c2); //调用拷贝构造函数 cout << "c2 半径为 " << c2.radius << " 时,圆周长 = " << c2.circumference() << ", 圆面积 = " << c2.area() << endl; cout << "c3 半径为 " << c3.radius << " 时,圆周长 = " << c3.circumference() << ", 圆面积 = " << c3.area() << endl; //注意:调用默认构造函数时,不要加() // Circle c1(); //此处会认为是一个函数声明,而不是创建对象,编译时报错 //2、显示法 Circle c4; //调用默认构造函数,即无参的构造函数 Circle c5 = Circle(3); //调用有参构造函数 Circle c6 = Circle(c5); //调用拷贝构造函数 Circle(5); //匿名对象:当前行执行结束后,系统会立即回收掉匿名对象 cout << "匿名对象执行结束后,系统立即回收掉匿名对象" << endl; //注意:不要使用拷贝构造函数初始化匿名对象 //Circle(c6); //此处编译器会认为 Circle c6; 即创建一个对象 c6,但c6对象已存在,所以编译时会报错 //3、隐式转换法 Circle c7 = 6; //相当于 Circle c7 = Circle(6); 调用有参构造函数 Circle c8 = c7;//相当于 Circle c8 = Circle(c7); 调用拷贝构造函数 } int main() { //类和对象 - 构造函数与析构函数 f1(); system("pause"); return 0; //程序 }
输出结果
5.3、拷贝构造函数调用时机Circle 无参构造函数调用
Circle 有参构造函数调用,参数 _radius = 3
Circle 拷贝构造函数调用,常量引用参数拷贝结果 radius = 3
c2 半径为 3 时,圆周长 = 18.84, 圆面积 = 88.7364
c3 半径为 3 时,圆周长 = 18.84, 圆面积 = 88.7364
Circle 无参构造函数调用
Circle 有参构造函数调用,参数 _radius = 3
Circle 拷贝构造函数调用,常量引用参数拷贝结果 radius = 3
Circle 有参构造函数调用,参数 _radius = 5
~Circle 析构函数调用
匿名对象执行结束后,系统立即回收掉匿名对象
Circle 有参构造函数调用,参数 _radius = 6
Circle 拷贝构造函数调用,常量引用参数拷贝结果 radius = 6
~Circle 析构函数调用
~Circle 析构函数调用
~Circle 析构函数调用
~Circle 析构函数调用
~Circle 析构函数调用
~Circle 析构函数调用
~Circle 析构函数调用
~Circle 析构函数调用
- 使用一个已经创建完的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
#includeusing namespace std; #define PI 3.14 //类 - 圆 class Circle { public: //半径 int radius; //构造函数 - 无参 Circle() { cout << "Circle 无参构造函数调用" << endl; } //构造函数 - 有参 Circle(int _radius) { radius = _radius; cout << "Circle 有参构造函数调用,参数 _radius = " << _radius << endl; } //拷贝构造函数 Circle(const Circle& _circle) { //将参数拷贝给自己成员 radius = _circle.radius; cout << "Circle 拷贝构造函数调用,常量引用参数拷贝结果 radius = " << radius << endl; } //析构函数 ~Circle() { cout << "~Circle 析构函数调用" << endl; } //计算周长 double circumference() { return 2 * PI * radius; } //计算面积 double area() { return (PI * radius) * (PI * radius); } }; //1、使用一个已经创建完的对象来初始化一个新对象 void f1() { Circle c1(6); Circle c2(c1); } void callCircle(Circle _circle) { } //2、值传递的方式给函数参数传值 void f2() { Circle c1(5); callCircle(c1); } Circle returnCircle() { Circle c1(8); cout << "returnCircle函数内c1地址:" << &c1 << endl; return c1; } //3、以值方式返回局部对象 void f3() { Circle c1 = returnCircle(); cout << "调用returnCircle函数后返回值地址:" << &c1 << endl; } int main() { //类和对象 - 构造函数 - 拷贝构造函数调用时机 cout << "--- 1、使用一个已经创建完的对象来初始化一个新对象 ---" << endl; f1(); cout << endl << "--- 2、值传递的方式给函数参数传值 ---" << endl; f2(); cout << endl << "--- 3、以值方式返回局部对象 ---" << endl; f3(); system("pause"); return 0; //程序 }
输出结果
5.4、构造函数调用规则--- 1、使用一个已经创建完的对象来初始化一个新对象 ---
Circle 有参构造函数调用,参数 _radius = 6
Circle 拷贝构造函数调用,常量引用参数拷贝结果 radius = 6
~Circle 析构函数调用
~Circle 析构函数调用--- 2、值传递的方式给函数参数传值 ---
Circle 有参构造函数调用,参数 _radius = 5
Circle 拷贝构造函数调用,常量引用参数拷贝结果 radius = 5
~Circle 析构函数调用
~Circle 析构函数调用--- 3、以值方式返回局部对象 ---
Circle 有参构造函数调用,参数 _radius = 8
returnCircle函数内c1地址:000000AB8FEFF3A4
Circle 拷贝构造函数调用,常量引用参数拷贝结果 radius = 8
~Circle 析构函数调用
调用returnCircle函数后返回值地址:000000AB8FEFF4E4
~Circle 析构函数调用
默认情况下,C++编译器至少给一个类添加3个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则:
- 如果用户定义了有参构造函数,C++不再提供默认无参构造函数,但是会提供默认拷贝构造函数
- 如果用户定义了拷贝构造函数,C++不再提供其他构造函数
#includeusing namespace std; //类 - 圆 //构造函数只有有参构造函数 class Circle { public: //半径 int radius; //构造函数 - 有参 Circle(int _radius) { radius = _radius; cout << "Circle 有参构造函数调用,参数 _radius = " << _radius << endl; } //析构函数 ~Circle() { cout << "~Circle 析构函数调用" << endl; } }; //类 - 圆 //构造函数只有拷贝构造函数 class Circle2 { public: //半径 int radius; //拷贝构造函数 Circle2(const Circle2& _circle) { radius = _circle.radius; cout << "Circle 拷贝构造函数调用,常量引用参数拷贝结果 radius = " << radius << endl; } //析构函数 ~Circle2() { cout << "~Circle 析构函数调用" << endl; } }; void f1() { //构造函数只有有参构造函数 //Circle c1; //使用无参构造函数创建对象,报错:不存在默认构造函数 Circle c2(5); //使用有参构造函数创建对象,正常 Circle c3(c2); //使用拷贝构造函数创建对象,正常 cout << "构造函数只有有参构造函数,使用拷贝构造函数创建对象时,c3.radius = " << c3.radius << endl; } void f2() { //构造函数只有拷贝构造函数,没有提供默认的构造函数 //Circle2 c1; //使用无参构造函数创建对象,报错:不存在默认构造函数 } int main() { //类和对象 - 构造函数调用规则 cout << "--- 1、如果用户定义了有参构造函数,C++不再提供默认无参构造函数,但是会提供默认拷贝构造函数 ---" << endl; f1(); cout << endl << "--- 2、如果用户定义了拷贝构造函数,C++不再提供其他构造函数 ---" << endl; f2(); system("pause"); return 0; //程序 }
输出结果
5.5、深拷贝与浅拷贝--- 1、如果用户定义了有参构造函数,C++不再提供默认无参构造函数,但是会提供默认拷贝构造函数 ---
Circle 有参构造函数调用,参数 _radius = 5
构造函数只有有参构造函数,使用拷贝构造函数创建对象时,c3.radius = 5
~Circle 析构函数调用
~Circle 析构函数调用--- 2、如果用户定义了拷贝构造函数,C++不再提供其他构造函数 ---
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
注:
如果属性有堆区开辟空间的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
浅拷贝存在的问题
以下一成员属性在堆区开辟空间
#includeusing namespace std; //类 - 圆 class Circle { public: //半径 int radius; //长度(仅作示例) int* length; Circle(int _radius, int _length) { radius = _radius; length = new int(_length); //堆区开辟空间 } //析构函数 ~Circle() { //将堆区开辟的空间释放 if (length != NULL) { delete length; length = NULL; } cout << "~Circle 析构函数调用" << endl; } }; int main() { //类和对象 - 构造函数 - 深拷贝与浅拷贝 cout << "--- 浅拷贝存在的问题 ---" << endl; Circle c1(2, 5); Circle c2(c1); cout << "--- 析构函数手动释放堆区空间时,抛出异常 ---" << endl; //原因:默认的拷贝构造函数为浅拷贝,即简单复制,当c2空间释放了,length指针堆上分配的空间也被释放,再释放c1时,调用析构函数,释放length时,由于已经被释放,因此抛异常 system("pause"); return 0; //程序 }
输出结果后,关闭程序,抛出异常:
原因:默认的拷贝构造函数为浅拷贝,即简单复制,当c2空间释放了,length指针堆上分配的空间也被释放,再释放c1时,调用析构函数,释放length时,由于已经被释放,再次重复释放,因此抛异常。
解决方案
提供拷贝构造函数,实现深拷贝
#include5.6、初始化列表using namespace std; //类 - 圆 class Circle { public: //半径 int radius; //长度(仅作示例) int* length; Circle(int _radius, int _length) { radius = _radius; length = new int(_length); //堆区开辟空间 } //解决浅拷贝的问题:拷贝构造函数实现深拷贝 Circle(const Circle& _circle) { radius = _circle.radius; //length = _circle.length; //编译器默认提供浅拷贝 length = new int(*_circle.length); //堆区开辟空间,实现深拷贝 } //析构函数 ~Circle() { //将堆区开辟的空间释放 if (length != NULL) { delete length; length = NULL; } cout << "~Circle 析构函数调用" << endl; } }; int main() { //类和对象 - 构造函数 - 深拷贝与浅拷贝 cout << "--- 解决浅拷贝存在的问题:拷贝构造函数实现深拷贝 ---" << endl; Circle c1(2, 5); Circle c2(c1); system("pause"); return 0; //程序 }
初始化列表用来初始化属性
语法:
构造函数(): 属性1(值1),属性2(值2)...{} //初始化默认值
构造函数(参数列表): 属性1(参数1),属性2(参数2)...{}
#includeusing namespace std; //类 - 圆 - 初始化列表方式一 class Circle { public: //半径 int radius; //初始化列表,为成员属性设置初始值 Circle() :radius(5) { cout << "初始化列表Circle() :radius(5), radius = " << radius << endl; } }; //类 - 圆 - 初始化列表方式二 class Circle2 { public: //半径 int radius; //初始化列表,为成员属性设置初始值 Circle2(int _radius) :radius(_radius) { cout << "初始化列表Circle2(int _radius) :radius(_radius), radius = " << radius << endl; } }; int main() { //类和对象 - 构造函数 - 初始化列表 Circle c1; Circle2 c2(3); system("pause"); return 0; //程序 }
输出结果
5.7、类对象作为类成员,构造函数与析构函数先后执行顺序初始化列表Circle() :radius(5), radius = 5
初始化列表Circle2(int _radius) :radius(_radius), radius = 3
- 构造顺序:先调用对象成员的构造函数,再调用本类的构造函数
- 析构顺序:与构造顺序相反,即先调用本类的析构函数,再调用对象成员的析构函数
#includeusing namespace std; class Point { public: int x; int y; //构造函数 Point(int _x, int _y) :x(_x), y(_y) { cout << "Point 构造函数" << endl; } //析构函数 ~Point() { cout << "Point 析构函数" << endl; } }; //类 - 圆 class Circle { public: //半径 int radius; //圆心 - 类对象成员 Point point; //构造函数 // Point point(_x, _y); Circle(int _radius, int _x, int _y):radius(_radius),point(_x,_y) { cout << "Circle 构造函数" << endl; } //析构函数 ~Circle() { cout << "Circle 析构函数" << endl; } }; int main() { //类和对象 - 构造函数 - 类对象作为类成员,构造函数与析构函数先后执行顺序 Circle(2, 3, 4); system("pause"); return 0; //程序 }
输出结果
Point 构造函数
Circle 构造函数
Circle 析构函数
Point 析构函数



