c++基础学习笔记——04-c++day03
在学习c++基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
04-c++day03
目录:
一、类和对象
1、面向对象程序设计案例
练习1:设计立方体类(Cube)
练习2:点和圆的关系
2、对象的构造和析构
(1)初始化和清理
(2)构造函数的分类及调用
(3)拷贝构造函数调用时机
(4)构造函数的调用规则
(5)深拷贝和浅拷贝
(6)初始化列表
(7)类对象作为类成员的案例
(8)explicit关键字
(9)new运算符的使用
二、总结
一、类和对象
1、面向对象程序设计案例
练习1:设计立方体类(Cube)
求出立方体的面积( 2*a*b + 2*a*c + 2*b*c )和体积( a * b * c),分别用全局函数和成员函数判断两个立方体是否相等。
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 /* 6 设计立方体类(Cube), 7 求出立方体的面积( 2*a*b + 2*a*c + 2*b*c )和体积( a * b * c), 8 分别用全局函数和成员函数判断两个立方体是否相等。 9 */ 10 11 class Cube 12 { 13 public: 14 15 //设置长 16 void setL(int l) 17 { 18 m_L = l; 19 } 20 //获取长 21 int getL() 22 { 23 return m_L; 24 } 25 //设置宽 26 void setW(int w) 27 { 28 m_W = w; 29 } 30 //获取宽 31 int getW() 32 { 33 return m_W; 34 } 35 //设置高 36 void setH(int h) 37 { 38 m_H = l; 39 } 40 //获取高 41 int getH() 42 { 43 return m_H; 44 } 45 46 //求立方体的面积 47 void getCubeS() 48 { 49 cout << "立方体的面积:" << 2* m_L * m_W + 2 m_L * m_H + 2 m_W * m_H << endl; 50 } 51 //求立方体的体积 52 void getCubeV() 53 { 54 cout << "立方体的体积:" << m_L * m_W * m_H << endl; 55 } 56 57 //通过成员函数判断是否相等 58 bool compareCube(Cube &cub) 59 { 60 bool ret = m_L == cub.getL() && m_W == cub.getW() && m_H == cub.getH(); 61 return ret; 62 } 63 64 65 private: 66 int m_L;//长 67 int m_W;//宽 68 int m_H;//高 69 }; 70 71 //全局函数判断 两个立方体是否相等 72 bool compareCubeByClass(Cube &cub1, Cube &cub2) 73 { 74 if(cub1.getL() == cub2.getL() && cub1.getW() == cub2.getW() && cub1.getH() == cub2.getH() ) 75 { 76 return true; 77 } 78 return false; 79 } 80 81 82 void test01() 83 { 84 Cube c1; 85 c1.setL(10); 86 c1.setW(10); 87 c1.setH(10); 88 89 c1.getCubeS(); 90 c1.getCubeV(); 91 92 93 Cube c2; 94 c2.setL(10); 95 c2.setW(10); 96 c2.setH(10); 97 98 //通过全局函数判断两个立方体是否相等 99 bool ret = compareCube(c1, c2); 100 if(ret) 101 { 102 cout << "c1和c2是相等的!" << endl; 103 } 104 else 105 { 106 cout << "c1和c2是不相等的!" << endl; 107 } 108 109 //通过成员函数判断是否相等 110 bool ret2 = c1.compareCubeByClass(c2); 111 if(ret2) 112 { 113 cout << "通过成员判断:c1和c2是相等的!" << endl; 114 } 115 else 116 { 117 cout << "通过成员判断:c1和c2是不相等的!" << endl; 118 } 119 } 120 121 int main() 122 { 123 test01(); 124 125 system("pause"); 126 return EXIT_SUCCESS; 127 }
问题:为什么const刚刚不能添加?没法保证成员函数里是否修改了成员属性
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 /* 6 设计立方体类(Cube), 7 求出立方体的面积( 2*a*b + 2*a*c + 2*b*c )和体积( a * b * c), 8 分别用全局函数和成员函数判断两个立方体是否相等。 9 */ 10 11 class Cube 12 { 13 public: 14 15 //设置长 16 void setL(int l) 17 { 18 m_L = l; 19 } 20 //获取长 21 int getL() const 22 { 23 return m_L; 24 } 25 //设置宽 26 void setW(int w) 27 { 28 m_W = w; 29 } 30 //获取宽 31 int getW() 32 { 33 return m_W; 34 } 35 //设置高 36 void setH(int h) 37 { 38 m_H = l; 39 } 40 //获取高 41 int getH() 42 { 43 return m_H; 44 } 45 46 //求立方体的面积 47 void getCubeS() 48 { 49 cout << "立方体的面积:" << 2* m_L * m_W + 2 m_L * m_H + 2 m_W * m_H << endl; 50 } 51 //求立方体的体积 52 void getCubeV() 53 { 54 cout << "立方体的体积:" << m_L * m_W * m_H << endl; 55 } 56 57 //通过成员函数判断是否相等 58 bool compareCube(Cube &cub) 59 { 60 bool ret = m_L == cub.getL() && m_W == cub.getW() && m_H == cub.getH(); 61 return ret; 62 } 63 64 65 private: 66 int m_L;//长 67 int m_W;//宽 68 int m_H;//高 69 }; 70 71 //为什么const刚刚不能添加?没法保证成员函数里是否修改了成员属性 72 void func(const Cube &cub) 73 { 74 cub.getL(); 75 } 76 77 //全局函数判断 两个立方体是否相等 78 bool compareCubeByClass(Cube &cub1, Cube &cub2) 79 { 80 if(cub1.getL() == cub2.getL() && cub1.getW() == cub2.getW() && cub1.getH() == cub2.getH() ) 81 { 82 return true; 83 } 84 return false; 85 } 86 87 void test01() 88 { 89 Cube c1; 90 c1.setL(10); 91 c1.setW(10); 92 c1.setH(10); 93 94 c1.getCubeS(); 95 c1.getCubeV(); 96 97 98 99 Cube c2; 100 c2.setL(10); 101 c2.setW(10); 102 c2.setH(10); 103 104 //通过全局函数判断两个立方体是否相等 105 bool ret = compareCube(c1, c2); 106 if(ret) 107 { 108 cout << "c1和c2是相等的!" << endl; 109 } 110 else 111 { 112 cout << "c1和c2是不相等的!" << endl; 113 } 114 115 //通过成员函数判断是否相等 116 bool ret2 = c1.compareCubeByClass(c2); 117 if(ret2) 118 { 119 cout << "通过成员函数判断:c1和c2是相等的!" << endl; 120 } 121 else 122 { 123 cout << "通过成员函数判断:c1和c2是不相等的!" << endl; 124 } 125 } 126 127 int main() 128 { 129 test01(); 130 131 system("pause"); 132 return EXIT_SUCCESS; 133 }
练习2:点和圆的关系
设计一个圆形类(AdvCircle),和一个点类(Point),计算点和圆的关系。
假如圆心坐标为x0, y0, 半径为r,点的坐标为x1, y1:
1)点在圆上:(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) == r*r
2)点在圆内:(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) < r*r
3)点在圆外:(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) > r*r
整个文件编写:
整个文件_立方体案例.cpp
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 /* 6 设计一个圆形类(AdvCircle),和一个点类(Point),计算点和圆的关系。 7 假如圆心坐标为x0, y0, 半径为r,点的坐标为x1, y1 8 */ 9 10 //点类 11 class Point 12 { 13 public: 14 void setX(int x) 15 { 16 m_X = x; 17 } 18 void setY(int y) 19 { 20 m_Y = y; 21 } 22 int getX 23 { 24 return m_X; 25 } 26 int getY 27 { 28 return m_Y; 29 } 30 31 private: 32 int m_X; 33 int m_Y; 34 }; 35 //圆类 36 class Circle 37 { 38 public: 39 40 //设置半径 41 void setR(int r) 42 { 43 m_R = r; 44 } 45 //获取半径 46 int getR() 47 { 48 return m_R; 49 } 50 //设置圆心 51 void setCenter(Point p) 52 { 53 m_Center = p; 54 } 55 //获取圆心 56 Point getCenter() 57 { 58 return m_Center; 59 } 60 61 //利用成员函数判断点和圆关系c1.isInCircleByClass(p1); 62 void isInCircleByClass(Point &p) 63 { 64 int distance = (m_Center.getX() - p.getX()) * (m_Center.getCenter().getX() - p.getX()) + (m_Center.getY() - p.getY()) * (m_Center.getY() - p.getY()); 65 int rDistance = m_R * m_R; 66 if(rDistance == distance) 67 { 68 cout << "成员函数:点在圆上" << endl; 69 } 70 else if(rDistance > distance) 71 { 72 cout << "成员函数:点在圆内" << endl; 73 } 74 else 75 { 76 cout << "成员函数:点在圆外" << endl; 77 } 78 } 79 80 81 private: 82 int m_R;//半径 83 Point m_Center;//圆心 84 }; 85 86 //利用全局函数判断点和圆的关系 87 void isInCircle(Circle &c, Point &p) 88 { 89 //获取圆心和点的距离 的平方 90 int distance = (c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) + (c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY()); 91 int rDistance = c.getR() * c.getR(); 92 93 if(rDistance == distance) 94 { 95 cout << "点在圆上" << endl; 96 } 97 else if(rDistance > distance) 98 { 99 cout << "点在圆内" << endl; 100 } 101 else 102 { 103 cout << "点在圆外" << endl; 104 } 105 } 106 107 108 109 void test01() 110 { 111 Point p1; 112 p1.setX(10); 113 p1.setY(10); 114 115 Circle c1; 116 Point center; 117 center.setX(10); 118 center.setY(0); 119 c1.setR(10); 120 c1.setCenter(center); 121 //利用全局判断点和圆的关系 122 isInCircle(c1, p1); 123 124 //利用成员函数判断点和圆的关系 125 c1.isInCircleByClass(p1); 126 } 127 128 int main() 129 { 130 test01(); 131 132 system("pause"); 133 return EXIT_SUCCESS; 134 }
分文件编写:
分文件_立方体案例.cpp
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 #include"point.h" 5 #include"circle.h" 6 /* 7 设计一个圆形类(AdvCircle),和一个点类(Point),计算点和圆的关系。 8 假如圆心坐标为x0, y0, 半径为r,点的坐标为x1, y1 9 */ 10 11 //利用全局函数判断点和圆的关系 12 void isInCircle(Circle &c, Point &p) 13 { 14 //获取圆心和点的距离 的平方 15 int distance = (c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) + (c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY()); 16 int rDistance = c.getR() * c.getR(); 17 18 if(rDistance == distance) 19 { 20 cout << "点在圆上" << endl; 21 } 22 else if(rDistance > distance) 23 { 24 cout << "点在圆内" << endl; 25 } 26 else 27 { 28 cout << "点在圆外" << endl; 29 } 30 } 31 32 33 34 void test01() 35 { 36 Point p1; 37 p1.setX(10); 38 p1.setY(10); 39 40 Circle c1; 41 Point center; 42 center.setX(10); 43 center.setY(0); 44 c1.setR(10); 45 c1.setCenter(center); 46 //利用全局判断点和圆的关系 47 isInCircle(c1, p1); 48 49 //利用成员函数判断点和圆的关系 50 c1.isInCircleByClass(p1); 51 } 52 53 int main() 54 { 55 test01(); 56 57 system("pause"); 58 return EXIT_SUCCESS; 59 }
point.h
1 #pragma once 2 #include<iostream> 3 using namespace std; 4 5 //点类 6 class Point 7 { 8 public: 9 void setX(int x); 10 void setY(int y); 11 int getX(); 12 int getY(); 13 14 15 private: 16 int m_X; 17 int m_Y; 18 };
point.cpp
1 #include"point.h" 2 3 void Point::setX(int x) 4 { 5 m_X = x; 6 } 7 void Point::setY(int y) 8 { 9 m_Y = y; 10 } 11 int Point::getX() 12 { 13 return m_X; 14 } 15 int Point::getY() 16 { 17 return m_Y; 18 }
circle.h
1 #pragma once 2 #include<iostream> 3 using namespace std; 4 #include"point.h" 5 6 //圆类 7 class Circle 8 { 9 public: 10 11 //设置半径 12 void setR(int r); 13 //获取半径 14 int getR(); 15 //设置圆心 16 void setCenter(Point p); 17 //获取圆心 18 Point getCenter(); 19 20 //利用成员函数判断点和圆关系c1.isInCircleByClass(p1); 21 void isInCircleByClass(Point &p); 22 23 24 private: 25 int m_R;//半径 26 Point m_Center;//圆心 27 };
circle.cpp
1 #include"circle.h" 2 3 //设置半径 4 void Circle::setR(int r) 5 { 6 m_R = r; 7 } 8 //获取半径 9 int Circle::getR() 10 { 11 return m_R; 12 } 13 //设置圆心 14 void Circle::setCenter(Point p) 15 { 16 m_Center = p; 17 } 18 //获取圆心 19 Point Circle::getCenter() 20 { 21 return m_Center; 22 } 23 24 //利用成员函数判断点和圆关系c1.isInCircleByClass(p1); 25 void Circle::isInCircleByClass(Point &p) 26 { 27 int distance = (m_Center.getX() - p.getX()) * (m_Center.getCenter().getX() - p.getX()) + (m_Center.getY() - p.getY()) * (m_Center.getY() - p.getY()); 28 int rDistance = m_R * m_R; 29 if(rDistance == distance) 30 { 31 cout << "成员函数:点在圆上" << endl; 32 } 33 else if(rDistance > distance) 34 { 35 cout << "成员函数:点在圆内" << endl; 36 } 37 else 38 { 39 cout << "成员函数:点在圆外" << endl; 40 } 41 }
2、对象的构造和析构
(1)初始化和清理
背景:
我们大家在购买一台电脑或者手机,或者其他的产品,这些产品都有一个初始设置,也就是这些产品对被创建的时候会有一个基础属性值。那么随着我们使用手机和电脑的时间越来越久,那么电脑和手机会慢慢被我们手动创建很多文件数据,某一天我们不用手机或电脑了,那么我们应该将电脑或手机中我们增加的数据删除掉,保护自己的信息数据。
从这样的过程中,我们体会一下,所有的事物在起初的时候都应该有个初始状态,当这个事物完成其使命时,应该及时清除外界作用于上面的一些信息数据。
那么我们c++中OO思想也是来源于现实,是对现实事物的抽象模拟,具体来说,当我们创建对象的时候,这个对象应该有一个初始状态,当对象销毁之前应该销毁自己创建的一些数据。
对象的初始化和清理也是两个非常重要的安全问题,一个对象或者变量没有初始时,对其使用后果是未知,同样的使用完一个变量,没有及时清理,也会造成一定的安全问题。c++为了给我们提供这种问题的解决方案,构造函数和析构函数,这两个函数将会被编译器自动调用,完成对象初始化和对象清理工作。
无论你是否喜欢,对象的初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事,所以编写类就应该顺便提供初始化函数。
为什么初始化操作是自动调用而不是手动调用?既然是必须操作,那么自动调用会更好,如果靠程序员自觉,那么就会存在遗漏初始化的情况出现。
练习:
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 class Person 6 { 7 public: 8 //构造函数写法 9 //与类名相同,没有返回值,不写void,可以发生重载(可以有参数) 10 //构造函数由编译器自动调用,而不是手动,而且只会调用一次 11 Person() 12 { 13 cout << "构造函数的调用" << endl; 14 } 15 16 //析构函数写法 17 //与类名相同,类名前面加一个符号“~”,也没有返回值,不写void,不可以有参数(不能发生重载) 18 //自动调用,只会调用一次 19 ~Person() 20 { 21 cout << "析构函数的调用" << endl; 22 } 23 24 }; 25 26 27 void test01() 28 { 29 Person p1;//默认调用了构造和析构,是系统提供的两个空实现的函数 30 //这是开辟在栈上,所以可以看到“析构函数的调用” 31 } 32 33 int main() 34 { 35 test01(); 36 //Person p1;//看不到“析构函数的调用” 37 38 system("pause"); 39 return EXIT_SUCCESS; 40 }
(2)构造函数的分类及调用
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 //分类 6 //按照参数类型分类:无参构造函数(默认构造函数)、有参构造函数 7 //按照类型进行分类:普通构造函数、拷贝构造函数 8 9 class Person 10 { 11 public://构造和析构必须写在public下才可以调用到 12 Person()//默认、无参构造函数 13 { 14 cout << "默认构造函数调用" << endl; 15 } 16 17 Person(int a)//有参构造函数 18 { 19 cout << "有参构造函数调用" << endl; 20 } 21 22 //拷贝构造函数(加const不可修改) 23 Person(const Person &p) 24 { 25 m_Age = p.m_Age; 26 cout << "拷贝构造函数调用" << endl; 27 } 28 29 30 31 ~Person() 32 { 33 cout << "析构函数调用" << endl; 34 } 35 36 int m_Age; 37 }; 38 39 40 void test01() 41 { 42 //构造函数调用方式 43 //1.括号法调用 44 Person p1(1);//有参 45 p1.m_Age = 10; 46 Person p2(p1);//拷贝 47 48 cout << "p2的年龄" << p2.m_Age << endl; 49 50 Person p3;//默认构造函数,不要加(),Person p3();//编译器认为这行是函数声明 51 52 //2.显示法调用 53 Person p4 = Person(100); 54 Person p5 = Person(p4); 55 56 //Person(100)//叫匿名对象,匿名对象特定,如果编译器发现了对象是匿名的,那么在这行代码结束后就释放这个对象 57 58 //不能用拷贝构造函数初始化匿名对象 59 //Person(p5);//如果这么写,编译器认为你写成了Person p5;对象的声明,如果写成右值Person p6 = Person(p5);,那么可以 60 61 62 Person p7 = 100;//相当于调用了 Person p7 = Person(100);隐式类型转换 63 Person p8 = p7;//相当于 Person p8 = Person(p7); 64 } 65 66 int main() 67 { 68 test01(); 69 70 system("pause"); 71 return EXIT_SUCCESS; 72 }
(3)拷贝构造函数调用时机
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 class Person 6 { 7 public: 8 Person() 9 { 10 cout << "默认构造函数调用" << endl; 11 } 12 Person(int a) 13 { 14 cout << "有参构造函数调用" << endl; 15 } 16 Person(const Person &p)//不加&会进入死循环,一直调用拷贝构造 17 { 18 cout << "拷贝构造函数调用" << endl; 19 } 20 21 ~Person() 22 { 23 cout << "析构函数调用" << endl; 24 } 25 26 }; 27 28 //1.用已经创建好的对象来初始化新的对象 29 void test01() 30 { 31 Person p1; 32 p1.m_Age = 10; 33 34 Person p2(p1); 35 } 36 37 //2.以值传递的方式给函数参数传值 38 void doWork(Person p1)// Person p1 = Person(p); 39 { 40 41 } 42 void test02() 43 { 44 Person p; 45 p.m_Age = 10; 46 47 doWork(p); 48 } 49 50 //3.以值的方式返回局部对象 51 Person doWork2() 52 { 53 Person p1; 54 return p1; 55 } 56 57 void test03() 58 { 59 Person p = doWork2(); 60 } 61 //和Debug不同,Release下优化成什么样? 62 /* 63 Person p;//不调用默认构造 64 doWork2(p); 65 66 void doWork2(Person &p) 67 { 68 Person p1;//调用默认构造 69 } 70 */ 71 72 73 int main() 74 { 75 test01(); 76 77 system("pause"); 78 return EXIT_SUCCESS; 79 }
(4)构造函数的调用规则
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 class MyClass 6 { 7 public: 8 //MyClass() 9 //{ 10 // cout << "默认构造函数" << endl; 11 //} 12 MyClass(int a) 13 { 14 cout << "有参构造函数" << endl; 15 } 16 17 //MyClass(const MyClass &m) 18 //{ 19 // 20 //} 21 int m_A; 22 }; 23 24 //系统默认给一个类提供3个函数:默认构造函数、拷贝构造函数、析构函数 25 26 //1.当我们提供了有参构造函数,那么系统就不会给我们提供默认构造函数了 27 //但是,系统还会提供默认拷贝构造函数,进行简单的值拷贝 28 void test01() 29 { 30 MyClass c1(1); 31 c1.m_A = 10; 32 MyClass c2(c1); 33 cout << c2.m_A << endl; 34 } 35 36 //2.当我们提供了拷贝构造,那么系统就不会提供其他构造了 37 class MyClass2 38 { 39 public: 40 //MyClass2() 41 //{ 42 // cout << "默认构造函数" << endl; 43 //} 44 //MyClass2(int a) 45 //{ 46 // cout << "有参构造函数" << endl; 47 //} 48 49 MyClass2(const MyClass2 &m) 50 { 51 52 } 53 int m_A; 54 }; 55 56 void test02() 57 { 58 //MyClass2 c1;//如果自己不手动写默认构造,会报错 59 } 60 61 62 int main() 63 { 64 test01(); 65 66 system("pause"); 67 return EXIT_SUCCESS; 68 }
(5)深拷贝和浅拷贝
如果属性里有指向堆区空间的数据,那么简单的浅拷贝会导致重复释放内存的异常!
解决上述问题,需要我们自己提供构造函数(深拷贝)
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 class Person 6 { 7 public: 8 Person() 9 { 10 11 } 12 //初始化属性 13 Person(char* name, int age) 14 { 15 m_Name = (char*)malloc(strlen(name) + 1); 16 strcpy(m_Name, name); 17 18 m_age = age; 19 } 20 //拷贝构造,系统会提供默认拷贝构造,而且是简单的值拷贝 21 //自己提供拷贝构造,原因简单的浅拷贝会释放堆区空间两次,导致挂掉 22 //深拷贝 23 Person(const Person &p) 24 { 25 m_age = p.m_age; 26 m_Name = malloc(strlen(p.m_Name) + 1); 27 strcpy(m_Name, p.m_Name); 28 } 29 30 ~Person() 31 { 32 cout << "析构函数调用" << endl; 33 if(m_Name != NULL) 34 { 35 free(m_Name); 36 m_Name = NULL; 37 } 38 } 39 //姓名 40 char* m_Name; 41 //年龄 42 int m_age; 43 }; 44 45 46 47 void test01() 48 { 49 Person p1("敌法", 10); 50 Person p2(p1);//调用拷贝构造 51 52 53 } 54 55 int main() 56 { 57 test01(); 58 59 system("pause"); 60 return EXIT_SUCCESS; 61 }
(6)初始化列表
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 class Person 6 { 7 public: 8 //Person(){} 9 //有参构造 初始化数据 10 /* 11 Person(int a, int b, int c) 12 { 13 m_A = a; 14 m_B = b; 15 m_C = c; 16 } 17 */ 18 19 Person() :m_A(10), m_B(20), m_C(30)//写死了 20 { 21 22 } 23 //利用初始化列表来初始化数据 24 //语法:构造函数后面+:属性(参数),属性(参数)…… 25 Person(int a, int b, int c):m_A(a), m_B(b), m_C(c) 26 { 27 28 } 29 30 int m_A; 31 int m_B; 32 int m_C; 33 }; 34 35 void test01() 36 { 37 Person p1(10, 20, 30); 38 39 cout << "p1的m_A:" << p1.m_A << endl; 40 cout << "p1的m_B:" << p1.m_B << endl; 41 cout << "p1的m_C:" << p1.m_C << endl; 42 43 Person p2; 44 cout << "p2的m_A:" << p2.m_A << endl; 45 cout << "p2的m_B:" << p2.m_B << endl; 46 cout << "p2的m_C:" << p2.m_C << endl; 47 } 48 49 int main() 50 { 51 test01(); 52 53 system("pause"); 54 return EXIT_SUCCESS; 55 }
(7)类对象作为类成员的案例
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<string> using namespace std; class Phone { public: Phone() { cout << "手机的构造函数调用" << endl; } Phone(string name) { cout << "Phone的有参构造调用" << endl; m_PhoneName = name; } ~Phone() { cout << "手机的析构函数调用" << endl; } string m_PhoneName; }; class Game { public: Game() { cout << "Game的构造函数调用" << endl; } Game(string name) { cout << "Game的有参构造调用" << endl; m_GameName = name; } ~Game() { cout << "Game的析构函数调用" << endl; } string m_GameName; }; class Person { public: Person() { cout << "Person的构造函数调用" << endl; } Person(string name, string phoneName, string gameName) : m_Name(name), m_Phone(phoneName), m_Game(gameName) { cout << "Person的有参构造调用" << endl; //m_Name = name; } void playGame() { cout << m_Name << "拿着《" << m_Phone.m_PhoneName << "》牌手机,玩着《" << m_Game.m_GameName << "》游戏" << endl; } ~Person() { cout << "Person的析构函数调用" << endl; } string m_Name;//姓名 Phone m_Phone;//手机 Game m_Game;//游戏 }; //类对象作为类成员的时候,构造顺序先将类对象——构造,然后构造自己,析构的顺序是相反的 void test01() { //Person p; //p.m_Phone.m_PhoneName = "三星"; //p.m_Phone.m_GameName = "斗地主"; Person p("狗蛋", "苹果", "切水果"); p.playGame(); } int main() { test01(); system("pause"); return EXIT_SUCCESS; }
(8)explicit关键字
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 class MyString 6 { 7 public: 8 MyString(const char* str) 9 { 10 11 } 12 explicit MyString(int a) 13 { 14 15 } 16 17 char* mStr; 18 int mSize; 19 }; 20 21 void test01() 22 { 23 MyString str = "abc"; 24 MyString str2(10); 25 //MyString str3 = 10;//做什么用途?str2字符串为“10”,字符串的长度10 26 //隐式类型转换 MyString str3 = MyString(10) 27 //explicit关键字,防止隐式类型转换 28 } 29 30 int main() 31 { 32 test01(); 33 34 system("pause"); 35 return EXIT_SUCCESS; 36 }
(9)new运算符的使用
背景:
为了在运行时动态分配内存,c在他的标准库中提供了一些函数,malloc以及它的变种calloc和realloc,释放内存的free,这些函数是有效的、但是原始的,需要程序员理解和小心使用。为了使用c的动态内存分配函数在堆上创建一个类的实例,我们必须这样做.
问题:
1) 程序员必须确定对象的长度。
2) malloc返回一个void*指针,c++不允许将void*赋值给其他任何指针,必须强转。
3) malloc可能申请内存失败,所以必须判断返回值来确保内存分配成功。
4) 用户在使用对象之前必须记住对他初始化,构造函数不能显示调用初始化(构造函数是由编译器调用),用户有可能忘记调用初始化函数。
c的动态内存分配函数太复杂,容易令人混淆,是不可接受的,c++中我们推荐使用运算符new 和 delete.
C++中解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个称为new的运算符里。当用new创建一个对象时,它就在堆里为对象分配内存并调用构造函数完成初始化。
1 Person* person = new Person; 2 相当于: 3 Person* person = (Person*)malloc(sizeof(Person)); 4 if(person == NULL){ 5 return 0; 6 } 7 person->Init();
New操作符能确定在调用构造函数初始化之前内存分配是成功的,所有不用显式确定调用是否成功。
现在我们发现在堆里创建对象的过程变得简单了,只需要一个简单的表达式,它带有内置的长度计算、类型转换和安全检查。这样在堆创建一个对象和在栈里创建对象一样简单。
new表达式的反面是delete表达式。delete表达式先调用析构函数,然后释放内存。正如new表达式返回一个指向对象的指针一样,delete需要一个对象的地址。
delete只适用于由new创建的对象。
如果使用一个由malloc或者calloc或者realloc创建的对象使用delete,这个行为是未定义的。因为大多数new和delete的实现机制都使用了malloc和free,所以很可能没有调用析构函数就释放了内存。
如果正在删除的对象的指针是NULL,将不发生任何事,因此建议在删除指针后,立即把指针赋值为NULL,以免对它删除两次,对一些对象删除两次可能会产生某些问题。
使用new和delete在堆上创建数组非常容易。
当创建一个对象数组的时候,必须对数组中的每一个对象调用构造函数,除了在栈上可以聚合初始化,必须提供一个默认的构造函数。
数组所用的内存通常还包括“数组大小记录”,使得delete的时候知道应该调用几次析构函数。单一对象的话就没有这个记录。单一对象和数组对象的内存布局可理解为下图:
本图只是为了说明,编译器不一定如此实现,但是很多编译器是这样做的。
当我们使用一个delete的时候,我们必须让delete知道指针指向的内存空间中是否存在一个“数组大小记录”的办法就是我们告诉它。当我们使用delete[],那么delete就知道是一个对象数组,从而清楚应该调用几次析构函数。
结论:
如果在new表达式中使用[],必须在相应的delete表达式中也使用[].如果在new表达式中不使用[], 一定不要在相应的delete表达式中使用[].
练习:
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 using namespace std; 4 5 class Person 6 { 7 public: 8 Person() 9 { 10 cout << "默认构造调用" << endl; 11 } 12 Person(int a) 13 { 14 cout << "有参构造调用" << endl; 15 } 16 17 ~Person() 18 { 19 cout << "析构函数调用" << endl; 20 } 21 22 23 }; 24 25 void test01() 26 { 27 //Person p1;//栈区开辟 28 29 Person* p2 = new Person;//堆区开辟 30 31 //所有new出来的对象,都会返回该类型的指针 32 //malloc返回void*,还要强转 33 //malloc会调用构造吗?不会。new会调用构造 34 //new是运算符,malloc是函数 35 //释放堆区空间 36 //delete也是运算符,配合new使用;malloc配合free使用 37 delete p2; 38 39 } 40 41 void test02() 42 { 43 void* p = new Person; 44 //当用void* 接收new出来的指针,会出现释放的问题 45 delete p; 46 //无法释放p,所以避免这种写法 47 } 48 49 void test03() 50 { 51 //通过new开辟数组,一定会调用默认构造函数,所以一定要提供默认构造函数 52 Person* pArray = new Person[10]; 53 Person pArray[2] = { Person(1), Person(2)};//在栈上开辟数组,可以指定有参构造 54 55 //释放数组 56 delete [] pArray; 57 } 58 59 int main() 60 { 61 test01(); 62 63 system("pause"); 64 return EXIT_SUCCESS; 65 }
二、总结
1 立方体案例
2 点和圆关系案例
2.1 圆内的属性里有个其它的自定义数据类型 Point
2.2 三种关系判断
2.3 分文件编写
2.3.1 .h中写类的成员函数声明
2.3.2 .cpp中写成员函数实现
3 对象的初始化和清理
3.1 构造函数
3.1.1 没有返回值 没有void,类名相同,可以发生重载,可以有参数
3.2 析构函数
3.2.1 没有返回,没有void ,函数名称: ~类名,不可以发生重载,不可以有参数
3.3 系统会默认调用 构造函数和析构函数,而且只会调用一次
3.4 如果程序员没有提供构造和析构,系统会默认提供,空实现
4 构造函数的分类及调用
4.1 按照参数分类
4.1.1 无参构造(默认构造) 有参构造
4.2 按照类型分类
4.2.1 普通构造函数 拷贝构造函数
4.3 无参构造写法 和调用
4.3.1 Person p1 ; 注意不能写 Person p1() ,因为编译器认为这个是函数声明
4.4 有参构造写法 和调用
4.4.1 Person p2(10) 或者 Person p2 = Person(10)
4.4.2 Person(10) 匿名对象 ,执行当前行后就会释放这个对象
4.5 拷贝构造函数
4.5.1 Person( const Person & p )
4.5.2 Perons p1( p2) 或者 Person p1 = Person(p2)
4.5.3 不能用拷贝构造函数初始化匿名对象
4.5.3.1 如果写成 Person (p1) 这种写法等价于 Person p1
4.5.3.2 写到右值可以做拷贝构造函数
4.6 Person P = 100 隐式类型转换 相当于调用 Person p = Person(100)
5 拷贝构造函数调用时机
5.1 1、用已经创建好的对象来初始化新的对象
5.2 2、以值传递的方式给函数参数传值
5.3 3、以值方式返回局部对象
5.4 release 默认下会做优化
6 构造函数的调用规则
6.1 如果提供了有参的构造,那么系统就不会提供默认的构造了,但是会提供拷贝构造
6.2 如果提供了拷贝构造函数,那么系统就不会提供其他的构造函数了
7 深拷贝与浅拷贝
7.1 系统默认提供的拷贝构造 会进行简单的值拷贝
7.2 如果属性里有指向堆区空间的数据,那么简单的浅拷贝会导致重复释放内存的异常
7.3 解决上述问题,需要我们自己提供拷贝构造函数,进行深拷贝
8 初始化列表语法
8.1 在构造函数后面 + : 属性(值、参数), 属性(值、参数)…
9 类对象作为成员的案例
9.1 当类对象作为类的成员时候,构造顺序是先构造类对象的构造,然后构造自己,
9.2 析构顺序与构造相反
10 explicit关键字
10.1 作用:防止构造函数中的隐式类型转换
11 new 运算符 和 delete运算符
11.1 Person * p = new Person 会返回一个Person指针
11.2 默认调用构造函数,开辟空间,返回不是void* ,不需要强制转换
11.3 delete释放
11.4 new 对象 用void* 去接受,释放不了对象
11.5 new出来的是数组 ,如何释放? delete [] …
11.6 new出来的是数组,肯定会调用默认构造
在学习c++基础总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
posted on 2020-06-11 19:56 Alliswell_WP 阅读(267) 评论(0) 编辑 收藏 举报