目录
一、函数提高
1.1函数默认参数
1.2函数占位参数
1.3函数重载
1.3.1函数重载概述
1.3.2函数重载注意事项
二、类和对象
2.1封装
2.1.1封装的意义
2.1.2struct和class区别
2.1.3成员属性设置为私有
2.1.4封装案例
2.2对象的初始化和清理
2.2.1构造函数和析构函数
2.2.2构造函数的分类及调用
2.2.3拷贝构造函数调用时机
2.2.4构造函数调用规则
2.2.5深拷贝和浅拷贝
2.2.6初始化列表
2.2.7类对象作为类成员
2.2.8静态成员
2.3C++对象模型和this指针
2.3.1成员变量和成员函数分开存储
2.3.2this指针
未完待续。。。
一、函数提高
1.1函数默认参数
在C++中,函数的形参列表中的形参是可以有默认值的。
语法:返回值类型 函数名(参数 = 默认值){ }
注意事项:
1、如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值
2、如果函数声明有默认参数,函数实现就不能有默认参数(声明和实现只能有一个有默认参数)
#include<iostream>
using namespace std;//函数的默认参数
int func(int a, int b = 20, int c = 30) {return a + b + c;
}int main() {cout << func(10,30) << endl;//70system("pause");return 0;
}
1.2函数占位参数
C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
语法:返回值类型 函数名(数据类型){ }
#include<iostream>
using namespace std;//占位参数
void func(int a, int) {cout << "this is func" << endl;
}int main() {func(10, 10);system("pause");return 0;
}
1.3函数重载
1.3.1函数重载概述
作用:函数名可以相同,提高复用性
函数重载满足条件:
①同一个作用域下②函数名称相同③函数参数类型不同或者个数不同或者顺序不同
注意:函数的返回值不可以作为函数重载的条件
#include<iostream>
using namespace std;//函数重载
void func() {cout << "调用func" << endl;
}void func(int a) {cout << "调用func(int a)" << endl;
}void func(double b) {cout << "调用func(double b)" << endl;
}void func(int a,double b) {cout << "调用func(int a,double b)" << endl;
}void func(double b,int a) {cout << "调用func(double b,int a)" << endl;
}int main() {func();func(10);func(3.14);func(3, 3.14);func(3.14, 3);system("pause");return 0;
}
1.3.2函数重载注意事项
引用作为重载条件
函数重载碰到函数默认参数
#include<iostream>
using namespace std;//函数重载注意事项
//1、引用作为重载条件
void func(int& a) {cout << "调用func(int &a)" << endl;
}
void func(const int& a) {cout << "调用func(const int &a)" << endl;
}//2、函数重载碰到默认参数
void func2(int a,int b = 10) {cout << "调用func2(int a,int b = 10)" << endl;
}
void func2(int a) {cout << "调用func2(int a)" << endl;
}int main() {int a = 10;func(a);func(10);func2(10);//当函数重载碰到默认参数,报错system("pause");return 0;
}
二、类和对象
C++面向对象三大特性:封装、继承、多态
C++认为万事万物皆为对象,对象上有其属性和行为
2.1封装
2.1.1封装的意义
①将属性和行为作为一个整体,表现生活中的事物
②将属性和行为加以权限控制
封装意义1:
在设计类的时候,属性和行为写在一起,表现事物
语法:class 类名{ 访问权限:属性/行为}
示例1:设计一个圆类,求圆的周长
#include<iostream> using namespace std;const double PI = 3.14;class Circle { public://属性int m_r;//行为double calculateZC() {return 2 * PI * m_r;}};int main() {//创建具体的对象Circle c1;c1.m_r = 10;cout << "圆的周长:" << c1.calculateZC() << endl;system("pause");return 0; }
示例2:
设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号
#include<iostream> #include<string> using namespace std;class Student { public://属性string Name;int ID;//行为//打印姓名和学号void showNameID() {cout << "姓名:" << Name << "学号:" << ID << endl; }//设置姓名void setName(string name) {Name = name;}//设置学号void setID(int id) {ID = id;}};int main() {//创建具体的对象Student s1;s1.Name = "Jake";s1.ID = 1;s1.showNameID();Student s2;s2.setName("Marry");s2.setID(2);s2.showNameID();system("pause");return 0; }
封装意义2:
类在设计时,可以把属性和行为放在不同权限下,加以控制
访问权限有三种:
public 公共权限 类内可以访问,类外可以访问 protected 保护权限 类内可以访问,类外不可以访问(儿子可以访问父亲中的保护内容) private 私有权限 类内可以访问,类外不可以访问(儿子不可以访问父亲中的保护内容)
#include<iostream> #include<string> using namespace std;class Person { public:string Name; protected:string Car; private:int Password;public:void func() {Name = "张三";Car = "VWPOLO";Password = 123456;}};int main() {//创建具体的对象Person p1;p1.Name = "李四";//以下访问不到/*p1.Car = "YYC";p1.Password = 786808;*/system("pause");return 0; }
2.1.2struct和class区别
默认的访问权限不同
struct默认权限为公共public
class默认权限为私有private
#include<iostream> #include<string> using namespace std;class C1 {int m_A;//默认权限,私有 };struct C2 {int m_A;//默认权限,公共 };int main() {C1 c1;//c1.m_A = 100;//默认私有,不可访问C2 c2;c2.m_A = 100; //默认公共,可以访问system("pause");return 0; }
2.1.3成员属性设置为私有
优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,可以检测数据的有效性
#include<iostream> #include<string> using namespace std;class Person { private:string m_name;//姓名,可读可写int m_age;//年龄,可读可写string m_pet;//宠物,只写 public://设置姓名void setName(string name) {m_name = name;}//获取姓名string getName() {return m_name;}//设置年龄void setAge(int age) {if (age < 0 || age > 150) {m_age = 0;cout << "输入错误!" << endl;return;}m_age = age;}//获取年龄int getAge() {//m_age = 0;return m_age;}//设置宠物void setPet(string pet) {m_pet = pet;} };int main() {Person p1;p1.setName("张三");p1.setPet("哈哈");p1.setAge(10);cout <<"姓名为:"<< p1.getName() << endl;cout <<"年龄为:"<< p1.getAge() << endl;system("pause");return 0; }
2.1.4封装案例
案例一:设计立方体类(Cube),求出立方体的面积和体积,分别用全局函数和成员函数判断两个立方体是否相等。
#include<iostream> #include<string> using namespace std;class Cube { private:int m_L;//长int m_W;//宽int m_H;//高 public://设置长宽高void setL(int L) {m_L = L;}void setW(int W) {m_W = W;}void setH(int H) {m_H = H;}//获取长宽高int getL() {return m_L;}int getW() {return m_W;}int getH() {return m_H;}//获取立方体表面积double getS() {return ((m_L * m_W) + (m_L * m_H) + (m_W * m_H)) * 2;}//获取立方体体积double getV() {return m_L * m_W * m_H;}//通过成员函数,判断两个立方体是否相等bool isSameByClass(Cube &c) {if (m_L == c.getL() && m_W == c.getW() && m_H == c.getH()) {return true;}return false;} };//通过全局函数,判断两个立方体是否相等 bool isSame(Cube& c1, Cube& c2) {if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH()) {return true;}return false; }int main() {Cube c1;c1.setL(2);c1.setW(2);c1.setH(2);cout <<"表面积为:"<< c1.getS() << endl;cout << "体积为:" << c1.getV() << endl;cout << "==============================" << endl;Cube c2;c2.setL(2);c2.setW(2);c2.setH(2);//全局函数判断bool ret1 = isSame(c1, c2);//成员函数判断bool ret2 = c1.isSameByClass(c2);if (ret1 && ret2) {cout << "c1与c2相等" << endl;}else { cout << "c1与c2不相等" << endl;}system("pause");return 0; }
案例二:点和圆 的关系 ,设计一个圆类(Circle),和一个点类(Point),计算点和圆的关系。
分文件编写
point.h
#pragma once #include <iostream> using namespace std;//点类 class Point { private:int m_X;//x坐标int m_Y;//y坐标 public://设置获取X坐标void setX(int x);int getX();//设置获取Y坐标void setY(int y);int getY(); };
point.cpp
#include "point.h"//设置获取X坐标 void Point::setX(int x) {m_X = x; } int Point::getX() {return m_X; } //设置获取Y坐标 void Point::setY(int y) {m_Y = y; } int Point::getY() {return m_Y; }
circle.h
#pragma once #include <iostream> #include "point.h" using namespace std;//圆类 class Circle { private:int m_R;//半径Point m_Center;//圆心public://设置获取半径void setR(int r);int getR();//设置获取圆心void setCenter(Point center);Point getCenter();};
circle.cpp
#include "circle.h"//设置获取半径 void Circle::setR(int r) {m_R = r; } int Circle::getR() {return m_R; } //设置获取圆心 void Circle::setCenter(Point center) {m_Center = center; } Point Circle::getCenter() {return m_Center; }
main.cpp
#include<iostream> #include<string> #include "point.h" #include "circle.h" using namespace std;点类 //class Point { //private: // int m_X;//x坐标 // int m_Y;//y坐标 //public: // //设置获取X坐标 // void setX(int x) { // m_X = x; // } // int getX() { // return m_X; // } // //设置获取Y坐标 // void setY(int y) { // m_Y = y; // } // int getY() { // return m_Y; // } //}; // 圆类 //class Circle { //private: // int m_R;//半径 // Point m_Center;//圆心 // //public: // //设置获取半径 // void setR(int r) { // m_R = r; // } // int getR() { // return m_R; // } // //设置获取圆心 // void setCenter(Point center) { // m_Center= center; // } // Point getCenter() { // return m_Center; // } // //};//判断圆和点的关系 void isInCircle(Circle& c, Point& p) {//计算两点之间距离的平方int distance = (c.getCenter().getX() - p.getX())* (c.getCenter().getX() - p.getX()) + (c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());//计算半径的平方int rDistance = c.getR() * c.getR();//判断if (distance == rDistance) {cout << "点在圆上" << endl;}else if (distance < rDistance) {cout << "点在圆内" << endl;}else {cout << "点在圆外" << endl;} }int main() {//创建圆Circle c1;c1.setR(10);Point center;center.setX(10);center.setY(0);c1.setCenter(center);//创建点Point p1;p1.setX(10);p1.setY(9);isInCircle(c1, p1);system("pause");return 0; }
2.2对象的初始化和清理
2.2.1构造函数和析构函数
对象的初始化和清理是两个非常重要的安全问题
一个对象或者变量没有初始状态,其使用后果未知
使用完一个对象和变量,没有及时清理,也会造成安全问题
c++使用构造函数和析构函数解决上述问题,这两个函数被编译器自动调用,完成对象的初始化和清理工作
构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用
析构函数:主要作用于对象销毁前系统自动调用,执行一些清理工作
构造函数语法:
类名(){}
1、构造函数没有返回值也不写void
2、函数名称与类名相同
3、构造函数可以有参数,因此可以发生重载
4、程序在调用对象时候会自动调用构造,无需手动调用,且只调用一次
析构函数语法:
~类名(){}
1、析构函数没有返回值也不写void
2、函数名称与类名相同,且在名称前加上符号~
3、析构函数不可以有参数,因此不可以发生重载
4、程序在对象销毁前会自动调用析构,无需手动调用,且只调用一次
#include<iostream>
#include<string>
using namespace std;class Person {
public://1、构造函数 进行初始化操作Person() {cout << "Person构造函数的调用" << endl;}//2、析构函数 进行清理操作~Person(){cout << "Person析构函数的调用" << endl;}
};//构造和析构都是必须要有的实现,如果自己不提供,编译器会提供一个空实现的构造和析构
void test01() {Person p;//栈上的数据,test01执行之后会释放这个对象
}int main() {test01();system("pause");return 0;
}
2.2.2构造函数的分类及调用
两种分类方式:
-按参数分为:有参构造和无参构造
-按类型分为:普通构造和拷贝构造
三种调用方式:
-括号法
-显示法
-隐式转换法
#include<iostream>
#include<string>
using namespace std;class Person {
public:int age;//1、构造函数 //按照参数分类:有参构造 无参构造(默认构造)//按类型分为:普通构造 拷贝构造//普通构造函数Person() {cout << "Person无参构造函数的调用" << endl;}Person(int a) {age = a;cout << "Person有参构造函数的调用" << endl;}//拷贝构造函数//将传入的对象身上的属性全部拷贝到自身上Person(const Person &p) {age = p.age;cout << "Person拷贝构造函数的调用" << endl;}//2、析构函数 进行清理操作~Person(){cout << "Person析构函数的调用" << endl;}
};//调用
void test01() {//1、括号法//Person p1;//无参调用//Person p2(10);//有参调用//Person p3(p2);//拷贝构造函数调用//cout << "p2的年龄为:" << p2.age << endl;//cout << "p3的年龄为:" << p3.age << endl;//注意事项:调用默认构造函数时不要加()//Person p1();//编译器会以为是函数的声明//2、显示法//Person p1;//无参调用//Person p2 = Person(10);//有参调用//Person p3 = Person(p2);//拷贝构造函数调用//Person(10);//匿名对象,当前执行结束后,系统立即回收匿名对象//注意事项:不要用拷贝构造函数初始化匿名对象//Person(p3);//编译器会转换为Person p3;对象的声明//3、隐式转换法Person p4 = 10;//相当于写了Person p4 = Person(10);Person p5 = p4;//拷贝构造函数}int main() {test01();system("pause");return 0;
}
2.2.3拷贝构造函数调用时机
①使用一个已经创建完毕的对象来初始化一个新对象
②值传递的方式给函数参数传值
③以值方式返回局部对象
#include<iostream>
#include<string>
using namespace std;class Person {
public:int age;Person() {cout << "Person无参构造函数的调用" << endl;}Person(int a) {age = a;cout << "Person有参构造函数的调用" << endl;}//拷贝构造函数Person(const Person &p) {age = p.age;cout << "Person拷贝构造函数的调用" << endl;}//析构函数 进行清理操作~Person(){cout << "Person析构函数的调用" << endl;}
};//①使用一个已经创建完毕的对象来初始化一个新对象
void test01() {Person p1(10);Person p2(p1);cout << "p2的年龄:" << p2.age << endl;
}//②值传递的方式给函数参数传值
void doWork(Person p) {}
void test02() {Person p;doWork(p);
}//③以值方式返回局部对象
Person doWork2() {Person p1;return p1;
}
void test03() {Person p = doWork2();
}int main() {//test01();//test02();test03();system("pause");return 0;
}
2.2.4构造函数调用规则
默认情况下,c++编译器会至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝析构函数,对属性进行值拷贝
构造函数调用规则如下:
1、如果用户定义有参构造函数,则编译器不提供默认无参构造函数,但是会提供默认拷贝构造
2、如果用户定义拷贝构造函数,则编译器不提供其他构造函数
2.2.5深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
浅拷贝的问题:堆区的数据被两次释放,非法
解决方法:在堆区重新开辟一块空间,也就是深拷贝
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝的问题
#include<iostream> #include<string> using namespace std;class Person { public:int age;//年龄int* height;//身高Person() {cout << "Person无参构造函数的调用" << endl;}Person(int a,int h) {age = a;height = new int(h);cout << "Person有参构造函数的调用" << endl;}//自己实现拷贝构造函数,解决浅拷贝的问题Person(const Person& p) {cout << "Person拷贝构造函数的调用" << endl;age = p.age;//height = p.height;//编译器默认实现height = new int(*p.height);//深拷贝}//析构函数~Person() {//将堆区数据释放if (height != NULL) {delete height;height = NULL;}cout << "Person析构函数的调用" << endl;} };void test01() {Person p1(18,183);cout << "p1的年龄为:" << p1.age << endl;cout << "p1的身高为:" << *p1.height << endl;Person p2(p1);cout << "p2的年龄为:" << p2.age << endl;cout << "p2的身高为:" << *p2.height << endl;}int main() {test01();system("pause");return 0; }
2.2.6初始化列表
作用:C++提供了初始化列表语法,用来初始化属性
语法:构造函数():属性1(值1),属性2(值2),...{}
#include<iostream> #include<string> using namespace std;class Person { public:int m_A;int m_B;int m_C;//传统初始化/*Person(int a,int b,int c) {m_A = a;m_B = b;m_C = c;cout << "Person有参构造函数的调用" << endl;}*///初始化列表/*Person() :m_A(10), m_B(20), m_C(30) {}*/Person(int a,int b,int c) :m_A(a), m_B(b), m_C(c) {} };void test01() {//Person p1(1,3,2);//Person p1;Person p1(2,3,4);cout << "m_A为:" << p1.m_A << endl;cout << "m_B为:" << p1.m_B << endl;cout << "m_C为:" << p1.m_C << endl;}int main() {test01();system("pause");return 0; }
2.2.7类对象作为类成员
C++类中的成员可以是另一个类的对象,此成员称为对象成员
#include<iostream> #include<string> using namespace std;class Phone { public:string m_Pname;//品牌Phone(string pname){m_Pname = pname;} };class Person { public:string m_Name;//姓名Phone m_Phone;//手机//隐式 Person m_Phone = pName;Person(string name, string pname) :m_Name(name), m_Phone(pname) {}};void test01() {Person p("张三", "苹果15PROMAX");cout << p.m_Name << "拿着" << p.m_Phone.m_Pname << endl; }int main() {test01();system("pause");return 0; }
先有手机的构造,再有人的构造
先有人的析构,再有手机的析构
2.2.8静态成员
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。
静态成员分为:
①静态成员变量
-所有对象共享同一份数据-在编译阶段分配内存
-类内声明,类外初始化
#include<iostream> #include<string> using namespace std;class Person { public:static int m_A; private:static int m_B; }; int Person::m_A = 100;//类外声明 int Person::m_B = 200;//共享数据 void test01() {Person p1;cout << p1.m_A << endl;Person p2;p2.m_A = 200;cout << p1.m_A << endl;//cout << p1.m_B << endl;//私有,类外无法访问 }//两种访问方式 void test02() {//1.通过对象访问Person p1;cout << p1.m_A << endl;//2.通过类名访问cout << Person::m_A << endl; }int main() {//test01();test02();system("pause");return 0; }
②静态成员函数
-所有对象共享同一个函数
-静态成员函数只能访问静态成员变量
#include<iostream> #include<string> using namespace std;class Person { public://静态成员函数static void func() {m_A = 100;//静态成员函数可以访问静态成员变量//m_B = 99;//静态成员函数不可以访问非静态成员变量cout << "静态成员函数调用" << endl;}static int m_A;//静态成员变量int m_B;//非静态成员变量//静态成员函数也是有访问权限的 private:static void func2() {} }; int Person::m_A = 0;void test01() {//1.通过对象调用Person p1;p1.func();Person p2;p2.func();//2.通过类名调用Person::func();//Person::func2();//权限问题,不可访问 }int main() {test01();system("pause");return 0; }
2.3C++对象模型和this指针
2.3.1成员变量和成员函数分开存储
C++中,成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上面
#include<iostream> #include<string> using namespace std;class Person {int m_A;static int m_B;//静态成员变量不属于类的对象上void func(){}//非静态成员函数不属于类的对象上 }; int Person::m_B = 100;void test01() {Person p;//空对象占用的内存空间 //为了区分空对象占内存的位置//每个空对象会有一个独一无二的内存地址cout << "size of p = " << sizeof(p) << endl;//1 字节 }void test02() {Person p;cout << "size of p = " << sizeof(p) << endl;//4 字节 }//静态成员变量不属于类的对象上 void test03() {Person p;cout << "size of p = " << sizeof(p) << endl;//4 字节 }int main() {//test01();//test02();test03();system("pause");return 0; }
2.3.2this指针
this指针指向被调用的成员函数所属的对象
用途:
①当形参和成员变量同名时,可用this来区分
②在类的非静态成员函数中返回对象本身,可用return *this;