目录
一、函数提高
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;//70
system("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;