C++代码基础结构
手动编译代码并运行
Cout打印输出
数据类型-实型
在编译器中,会将"float = 3.14"中的“3.14”视为一个double类型的数据,再将其转换为float类型,这样就会多一步转换的操作,我们可以直接写成float = 3.14f,这样就省略了转换的操作
字符型
字符型只能使用‘ ’进行创建,用双引号会报错
创建字符型变量时候,单引号内只能有一个字符
\t水平制表符,算上前面的内容一共留下8个字符空间
水平制表(HT)(跳到下一个TAB位置)
算术运算符
两个小数是不可以做取模运算的,只有整形变量可以进行取模运算
程序的内存模型
new操作符
#include <iostream>
using namespace std;
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int* my_fuc()
{
int* p = new int(10);
return p;
}
void test1()
{
int * p = my_fuc();
cout << *p <<endl;
delete p;//内存已经被释放,再次访问就是非法操作,会报错(不一定会报错,但会乱码)
cout << *p <<endl;
}
//2、在堆区利用new开辟数组
void test2()
{
//创建10整型数据的数组,在堆区
int* arr = new int[10];//代表数组有10个元素
for(int i = 0;i<10;i++)
{
arr[i] = i;
}
for(int i = 0;i<10;i++)
{
cout << arr[i] << endl;
}
delete[] arr;
for(int i = 0;i<10;i++)
{
cout << arr[i] << endl;
}
}
int main(int argc, char** argv) {
test1();
test2();
return 0;
}
C++中的引用
1.引用的基本使用
#include <iostream>
using namespace std;
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
int a = 10;
int &b = a;
b = 20;
cout << a << endl;
return 0;
}
2.引用注意事项
1.引用必须初始化
2.引用在初始化后,不可以改变
#include <iostream>
using namespace std;
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
int a = 10;
int &b = a;
int c = 20;
b= c;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
return 0;
}
3. 引用做函数参数
#include <iostream>
using namespace std;
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void my_fuc(int &a ,int &b)
{
int temper = a;
a = b;
b = temper;
}
int main(int argc, char** argv) {
int a = 10;
int b = 20;
my_fuc(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单
4.引用做函数返回值
1.不要返回局部变量引用
#include <iostream>
using namespace std;
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int& my_fuc()
{
int a = 10;
return a;
}
int main(int argc, char** argv) {
int &ret = my_fuc();
cout << "ret = " << ret << endl;//第一次结果正确,是因为编译器做了保留
cout << "ret = " << ret << endl;//第二次结果错误,因为a的内存已经释放
cout << "ret = " << ret << endl;//第三次结果错误,因为a的内存已经释放
return 0;
}
2、函数的调用可以作为左值
#include <iostream>
using namespace std;
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int& my_fuc()
{
static int a = 10;//静态变量,存放在全局区,全局区上的数据在程序结束后系统释放
return a;
}
int main(int argc, char** argv) {
int &ret = my_fuc();
cout << "ret = " << ret << endl;
cout << "ret = " << ret << endl;
cout << "ret = " << ret << endl;
my_fuc() = 1000;//如果函数的返回值是引用,这个函数调用可以作为左值
cout << "ret = " << ret << endl;
cout << "ret = " << ret << endl;
return 0;
}
5.引用的本质
用const 修饰后,指针的指向是不可以修改的 ,但指向对象的值可以修改
6.常量引用
#include <iostream>
using namespace std;
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void my_fuc(const int &a)
{
//a = 1000;不可以进行修改操作,已经被const修饰了
cout << "a = " << a << endl;
}
int main(int argc, char** argv) {
//常量引用
//使用场景:用来修饰形参,防止误操作
//int a = 10;
//int &ref=10;//引用必须引一块合法的内存空间,比如栈区的数据或者堆区的数据,而字面量(存储在常量区)是不允许直接被引用的 ,因此该行代码错误
//不过可以加上const来直接引用字面量
//const int &ref=10;
//加上const之后I编译器将代码优化修改int temp = 10; const int & ref = temp;
//ref=20;//加入const之后变为只读,不可以修改
return 0;
}
函数高级
1.函数默认参数
#include <iostream>
using namespace std;
#include <stdio.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
//1、如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值
//2、如果函数声明有默认参数,函数实现就不能有默认参数,声明和实现只能有一个有默认参数
int fuc(int a = 10,int b = 10);
int fuc(int a,int b)
{
return a + b;
}
int main(int argc, char** argv) {
cout << fuc() << endl;
return 0;
}
2.函数占位参数
//占位参数
占位参数还可以有默认参数
void fuc(int a , int = 10)
{
cout << "Aekbi" << endl;
}
int main()
{
fuc(10,1);
return 0;
}
3.函数重载
void fuc()
{
cout << "Akebi" << endl;
}
void fuc(int a)
{
cout << "Komichi" << endl;
}
void fuc(double a)
{
cout << "Hobert" << endl;
}
void fuc(double a , int b)
{
cout << "VVV" << endl;
}
int main()
{
fuc(3.14,10);
return 0;
}
//函数重载的注意事项
//1、引用作为重载的条件 ,满足参数类型不同的函数重载条件
void func(int &a)//int&a =10;不合法
{
cout << "func(int&a)调用" << endl;
}
void func(const int &a)//cohst int &a = 10;合法
{
cout << "func(const int &a)调用" << endl;
}
//2、函数重载碰到默认参数,对于func2(10)会出现二义性报错
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(int&a)调用
func(10);//func(const int &a)调用
func2(10,20);
return 0;
}
类和对象
1.封装
#include <iostream>
using namespace std;
#include <stdio.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
const double Pi = 3.14;
class Circle
{
public:
int radlus;
double get_C()
{
return 2*Pi*radlus;
}
};//类的结尾大括号需要加上;
int main(int argc, char** argv) {
Circle C1;
C1.radlus = 10;
cout << "圆的周长" << C1.get_C() << endl;
return 0;
}
2.struct和class区别
3.成员属性设置为私有
class Person
{
//1、可以自己控制读写权限
//2、对于写可以检测数据有效性
private:
string P_name;//姓名可读可写
int P_age;//年龄只读也可以写(年龄必须在王 0 到150之间)
string P_ido;//偶像只写
public:
void setname(string name)
{
P_name = name;//属性名与函数参数名必须不同
}
string getname()
{
return P_name;
}
void setage(int age)
{
if(age< 0 || age>150)
{
cout << "输入不符合要求" << endl;
return;
}
P_age = age;
}
void setido(string ido)
{
P_ido = ido;
}
string getido()
{
return P_ido;
}
};
int main()
{
Person p1;
p1.setido("Akebi");
cout << "我推:" << p1.getido() << endl;
p1.setage(20);
p1.setname("Hobert");
return 0;
}
4.设计案例
(1)案例1
#include <iostream>
using namespace std;
#include <string>
//----------------------------------------------------------------------------------------------------------------------
class Cube
{
private:
int m_L;
int m_W;
int m_H;
public:
void set_Cube(int L,int W ,int H)
{
m_L = L;
m_W = W;
m_H = H;
}
int get_L()
{
return m_L;
}
int get_W()
{
return m_W;
}
int get_H()
{
return m_H;
}
void class_isSame(Cube c)
{
if(m_L == c.get_L() && m_W == c.get_W() && m_H == c.get_H())
{
cout << "两立方体一致" << endl;
}
else
{
cout << "两立方体不一致" << endl;
}
}
};
void judge_Cube(Cube &c1 , Cube &c2)//全局函数使用引用传参,可以像指针一样不必单独为形参创建变量
{
if (c1.get_L() == c2.get_L() && c1.get_W() == c2.get_W() && c1.get_H() == c2.get_H())
{
cout << "两立方体一致" << endl;
}
else
{
cout << "两立方体不一致" << endl;
}
}
int main()
{
Cube c1;
c1.set_Cube(10,10,10);
Cube c2;
c2.set_Cube(10,10,10);
judge_Cube(c1,c2);
return 0;
}
(2)案例2
main.cpp
#include <iostream>
using namespace std;
#include "point.h"
#include "Circe.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void is_Same(Circe c, point p)
{
int distance = (p.get_X() - c.get_point().get_X()) * (p.get_X() - c.get_point().get_X()) +
(p.get_Y() - c.get_point().get_Y()) * (p.get_Y() - c.get_point().get_Y());//这里记得加;
int radius = c.get_R() * c.get_R();
if(distance > radius)
{
cout << "点在圆外" << endl;
}
else if(distance == radius)
{
cout << "点在圆上" << endl;
}
else
{
cout << "点在圆内" << endl;
}
}
int main(int argc, char** argv) {
Circe c1;
c1.set_R(10);
point p1;
p1.set_X(10);
p1.set_Y(10);
point p2;
p2.set_X(10);
p2.set_Y(0);
c1.set_point(p2);
is_Same(c1,p1);
return 0;
}
point.cpp
#include "point.h"
void point::set_X(int x)
{
m_x = x;
}
int point::get_X()
{
return m_x;
}
void point::set_Y(int y)
{
m_y = y;
}
int point::get_Y()
{
return m_y;
}
point.h
#pragma once
#include <iostream>
using namespace std;
class point//类名的大小写要确保与后面一致
{
public:
void set_X(int x);
int get_X();
void set_Y(int y);
int get_Y();
private:
int m_x;
int m_y;
};
Circe.cpp
#include "Circe.h"
void Circe::set_R(int r)
{
m_r = r;
}
int Circe::get_R()
{
return m_r;
}
void Circe::set_point(point p)
{
Circe_heart = p;
}
point Circe::get_point()
{
return Circe_heart;
}
point.h
#pragma once
#include <iostream>
using namespace std;
#include "point.h"
class Circe
{
public:
void set_R(int r);
int get_R();
void set_point(point p);
point get_point();
private:
int m_r;
point Circe_heart;
};
5.对象的初始化和清理
析构代码,将堆区开辟的数据做释放的操作
class Person
{
public:
Person()
{
cout << "Akebi" << endl;
}
~Person()
{
cout << "Komichi" << endl;
}
};
int main(int argc, char** argv) {
Person p;
system("pause");
return 0;
}
6.构造函数的分类及调用
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Person
{
public:
Person()
{
cout << "无参" << endl;
}
Person(int a)
{
age = a;
cout << "有参" << endl;
}
Person(const Person &p)
{
age = p.age;
cout << "拷贝" << endl;
}
~Person()
{
cout << "析构函数" << endl;
}
int age;
};
void test01()
{
//1、括号法
Person p1;//默认构造函数调用
Person p2(10);//有参构造函数
Person p3(p2);//拷贝构造函数
cout << p3.age <<endl;
//注意事项
//调用默认构造函数时候,不要加()
//因为下面这行代码,编译器会认为是一个函数的声明,不会认为在创建对象
//Person p1()
//2、显示法
Person pl;
Person p2=Person(10);//有参构造
Person p3=Person(p2);//拷贝构造
//Person(10);//等号右边单独提C出来称为匿名对象,特点:当前行执行结束后,系统会立即回收掉匿名对象
//注意事项2
//不要利用拷贝构造函数初始化匿名对象编译器会认为Person(p3)== Person p3;报错重定义
//Person(p3) :
//3、隐式转换法
Person p4 = 10:相当于写了Person p4 = Person(10) ;有参构造
Person p5 = p4;/拷贝构造
}
int main(int argc, char** argv) {
test01();
system("pause");
return 0;
}
7.拷贝构造函数调用时机
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Person
{
public:
Person()
{
cout << "无参" << endl;
}
Person(int a)
{
age = a;
cout << "有参" << endl;
}
Person(const Person &p)
{
age = p.age;
cout << "拷贝" << endl;
}
~Person()
{
cout << "析构函数" << endl;
}
int age;
};
//利用拷贝来初始化一个新对象
void test01()
{
Person p1;
Person p2(p1);
}
//作形参传入时,p为一份拷贝
void my_fuc(Person p)
{
cout << "Akebi" << endl;
}
void test02()
{
Person p3;
my_fuc(p3);
}
//作函数返回值时,返回值为p4拷贝值
Person my_fuc()
{
Person p4;
return p4;
}
void test03()
{
Person p5 = my_fuc();
}
int main(int argc, char** argv) {
test01();
system("pause");
return 0;
}
8.构造函数调用规则
9.深拷贝与浅拷贝
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Person
{
public:
Person()
{
cout << "无参" << endl;
}
Person(int a, int b)
{
age = a;
height = new int(b);
cout << "有参" << endl;
}
Person(const Person &p)
{
age = p.age;//编译器默认为浅拷贝
height = new int(*p.height);//深拷贝操作
cout << "拷贝" << endl;
}
//堆区创建的数据由程序员手动释放
~Person()
{
if (height != NULL)
{
delete height;
height = NULL;
}
cout << "析构函数" << endl;
}
int age;
int* height;
};
//利用拷贝来初始化一个新对象
void test01()
{
Person p1(18,172);
Person p2(p1);
cout << "p1的age:" << p1.age << endl;
cout << "p1的height:" << *p1.height << endl;
cout << "p2的age:" << p2.age << endl;
cout << "p2的height:" << *p2.height << endl;
}
int main(int argc, char** argv) {
test01();
system("pause");
return 0;
}
10.初始化列表
class Person
{
public:
Person(int a ,int b , int c):m_a(a),m_b(b),m_c(c)
{
}
int m_a;
int m_b;
int m_c;
};
void test()
{
//Person p(1,2,3);//构造函数要这样调用 ,第一种
Person p = Person(10,20,30);//第二种
cout << p.m_a << endl;
cout << p.m_b << endl;
cout << p.m_c << endl;
}
int main()
{
test();
return 0;
}
11.类对象作为类成员
class Phone
{
public:
Phone(string name):Phonename(name)
{
cout << "Phone构造函数" << endl;
}
string Phonename;
};
class Person
{
public:
Person(string Per_name ,string Pho_name):Person_name(Per_name),P_Phone(Pho_name)
{
cout << "Person的构造函数" << endl;
}
string Person_name;
Phone P_Phone;
};
void test()
{
Person p("Akebi","Komichi");\
cout << p.Person_name << p.P_Phone.Phonename << endl;
}
int main()
{
test();
return 0;
}
11.静态成员
(1)静态成员变量
在编译阶段分配内存:在还没有运行exe文件之前就分配了内存,分配在全局区
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Person
{
public:
static int age;//静态变量需要写在public下 ,class的成员变量默认是private的,需要公共权限才可以类外声明
//静态成员变量也是有访问权限的
};
int Person::age = 18;//类作用域
void test01()
{
//静态成员变量不属于某个对象上,所有对象都共享同一份数据
//因此静态成员变量有两种访问方式
//1、通过对象进行访问
Person p1;
cout << p1.age << endl;
Person p2;
p2.age = 20;
cout << p1.age << endl;
//2、通过类名进行访问
cout << Person::age << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
(2)静态成员函数
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Person
{
public:
//静态成员函数也是有访问权限的,外部不可以访问private
static my_func()
{
age = 22;//静态成员函数可以访问静态成员变量
//静态成员函数不可以访问非静态成员变量,无法区分到底是哪个对象的成员变量
}
static int age;//静态变量需要写在public下 ,class的成员变量默认是private的,需要公共权限才可以类外声明
//静态成员变量也是有访问权限的
};
int Person::age = 18;//类作用域
void test01()
{
Person p1;
cout << p1.age << endl;
Person p2;
p2.age = 20;
cout << p1.age << endl;
p1.my_func();
cout << p1.age << endl;
cout << p2.age << endl;
Person::my_func();//直接通过类名访问函数
cout << Person::age << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
12.C++对象模型和this指针
(1)成员变量和成员函数分开存储
class Person
{
int age;//非静态成员变量属于类的对象上
static int hei;//静态成员变量不属于类对象上
void my_func();//非静态成员函数不属于类对象上
static void funv();//静态成员函数不属于类的对象上
};
//void test01()
//{
// Person p;
//
// cout << sizeof(p) << endl;//Person空类大小为1
//}
void test02()
{
Person p;
cout << sizeof(p) << endl;//4
}
int main()
{
// test01();
test02();
return 0;
}
(2)this指针概念
class Person
{
public:
Person(int age)
{
//this指针指向被调用的成员函数所属的对象
this -> age = age;//这样可以避免成员变量与形参同名导致错误的情况
}
Person& add_age(Person &p)//如果函数的返回值类型不是引用而是Person,则会导致链式编程失败,因为函数每次返回都创建的是新的类对象
//以引用的方式返回不会创建新的对象,而以值的方式返回则会创建新的对象
{
this -> age += p.age;
return *this;
//this指向p2的指针,而*this指向的就是p2这个对象本体
}
int age;
};
void test01()
{
Person p1(18);
cout << p1.age << endl;
Person p2(10);
//链式编程
p1.add_age(p2).add_age(p2);
cout << p1.age << endl; //这种形式的cout也是一种链式编程
}
int main()
{
test01();
return 0;
}
13.空指针访问成员函数
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Person
{
public:
void show_class()
{
cout << "this is a class" << endl;
}
void show_member()
{
cout << "age:" << m_age << endl;//在调用成员变量时,实际上形式是:this->m_age, 但此时p为空指针 ,this也是一个空指针
}
int m_age;
};
void test01()
{
Person* p = NULL;
p->show_class();
p->show_member();//该行代码会报错
}
int main(int argc, char** argv) {
test01();
return 0;
}
14.const修饰成员函数
class Person
{
public:
//this指针的本质是指针常量指针的指向是不可以修改的
//this指针不可以修改指针的指向的
//在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
//相当于const Person * const this
void my_add() const
{
//m_age = 10;
this -> m_b = 30;
}
void test()
{
}
int m_age = 10;
mutable int m_b = 20; //特殊变量,即使在常函数中,也可以修改这个值,加关键字mutable
};
int main()
{
//声明常量时需要为其指定一个初始值,要不就将 m_age 和 m_b 赋值,要不实例化对象时写成const Person p = Person();
//不然会报错
const Person p;//在对象前加const,变为常对象
p.my_add();
p.m_b = 50;//m_B是特殊值,在常对象下也可以修改
//p.test();//常对象不可以调用普通成员函数,因为普通成员函数可以修改属性
return 0;
}
15.友元(这里多看看,有点不太懂)
(1)全局函数做友元
class school
{
friend void gay();
public:
school()
{
school_lou = "楼";
school_fang = "房";
}
private:
string school_lou;
string school_fang;
};
void gay()
{
school sc;
cout << sc.school_lou << endl;
cout << sc.school_fang << endl;
}
void test01()
{
gay();
}
int main()
{
test01();
return 0;
}
(2)类做友元
class School;
class Gay
{
public:
Gay();
void visit();
School* school;
};
class School
{
friend class Gay;
public:
School()
{
school_lou = "楼";
school_fang = "房";
}
string school_lou;
private:
string school_fang;
};
Gay::Gay()
{
school = new School;//new School会调用其构造函数
}
void Gay::visit()
{
cout << school->school_lou << endl;
cout << school->school_fang << endl;
}
void test01()
{
Gay gg;
gg.visit();
}
int main()
{
test01();
return 0;
}
(3)成员函数做友元
class School;
class Gay
{
public:
Gay();
void visit01();
void visit02();
School* school;
};
class School
{
//告诉编译器Gay类中的visit成员函数是Building好朋友,可以访问私有内容
friend void Gay::visit01();
public:
School()
{
school_lou = "楼";
school_fang = "房";
}
string school_lou;
private:
string school_fang;
};
Gay::Gay()
{
school = new School;//new School会调用其构造函数
}
void Gay::visit01()
{
cout << school->school_lou << endl;
cout << school->school_fang << endl;
}
void Gay::visit02()
{
cout << school->school_lou << endl;
//cout << school->school_fang << endl;
}
void test01()
{
Gay gg;
gg.visit01();
gg.visit02();
}
int main()
{
test01();
return 0;
}
16.运算符重载
(1)加号运算符重载
(2)左移运算符重载
cout的数据类型为标准的输出流对象,且全局只能有一个,一次在多处地方使用的话要用引用
(3)递增运算符重载
class My_int
{
public:
My_int()
{
this->m_value = 0;
}
//前置递增重载
//前置返回值类型必须是引用,这样可以输出打印多次前置++,且始终作用于同一个变量,如:++(++i);
My_int& operator++()
{
this->m_value++;
return *this;
}
//后置递增重载
//为了区分前置和后置函数重载,需要使用占位参数int,只能使用int,使用其他的数据类型会报错
//这里返回值不能写成引用,因为这里的temp是局部变量在函数结束后释放堆区内存,再进行调用会报错
My_int operator++(int)
{
//先记录当时结果
//后递增
//最后将记录结果做返回
My_int temp = *this;//先将未++的保存起来
m_value++;
return temp;
}
int m_value;
};
void test01()
{
My_int i;
++i;
cout << i.m_value << endl;
i++;
cout << i.m_value << endl;
}
int main()
{
test01();
return 0;
}
(4)赋值运算符重载
class Person
{
public:
Person(int age)
{
m_age = new int(age);
}
~Person()
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
}
//重载赋值运算符
Person& operator=(Person &p)
{
//编译器是提供浅拷贝
//m_Age = p.m_Age;
//应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
m_age = new int(*p.m_age);//进行深拷贝
return *this;
}
int* m_age;
};
void test01()
{
Person p1(18);
Person p2(20);
Person p3(25);
p1 = p2 = p3;//这也是一种链式调用,连续赋值操作
cout << "p1的年龄:" << *p1.m_age << endl;
cout << "p2的年龄:" << *p2.m_age << endl;
cout << "p3的年龄:" << *p3.m_age << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
(5)关系运算符重载
class Person
{
public:
Person(string name,int age)
{
m_age = age;
m_name = name;
}
//重载==号
bool operator==(Person &p)
{
if(m_age == p.m_age && m_name == p.m_name)
{
return true;
}
else
{
return false;
}
}
//重载!=号
bool operator!=(Person &p)
{
if(m_age == p.m_age && m_name == p.m_name)
{
return false;
}
else
{
return true;
}
}
int m_age;
string m_name;
};
void test01()
{
Person p1("Akebi", 14);
Person p2("Komichi",15);
if(p1 == p2)
{
cout << "p1和p2相等" << endl;
}
else
{
cout << "p1和p2不相等" << endl;
}
if(p1 != p2)
{
cout << "p1和p2不相等" << endl;
}
else
{
cout << "p1和p2相等" << endl;
}
}
int main()
{
test01();
return 0;
}
(6)函数调用运算符重载
class My_print
{
public:
void operator()(string my_str)
{
cout << my_str << endl;
}
};
class My_add
{
public:
int operator()(int a ,int b)
{
return a+b;//这里不能够直接打印a+b的值,会出错
}
};
void test01()
{
My_print my_print;
//重载()的操作符也称为仿函数,写法不固定
my_print("Akebi Komichi");
My_add my_add;
int ret = my_add(100,200);
cout << "ret:" << ret << endl;
//匿名函数对象
cout << My_add()(10,10) << endl;//类型加上小括号会创建匿名对象 ,特点:当前行执行完了,立即被释放
}
int main(int argc, char** argv) {
test01();
return 0;
}
17.继承
(1)继承的基本语法
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
//继承的好处:减少重复代码
//语法:class子类:继承方式父类
//子类也称为派生类
//父类也称为基类
class school
{
public:
void name()
{
cout << "腊梅" << endl;
}
void address()
{
cout << "mountain" << endl;
}
};
class Akebi:public school
{
public:
void show_message()
{
cout << "age:" << m_age << endl;
}
int m_age = 14;
};
class Komichi:public school
{
public:
void show_message()
{
cout << "age:" << m_age << endl;
}
int m_age = 14;
};
void test01()
{
Akebi akebi;
akebi.show_message();
akebi.name();
akebi.address();
Komichi komichi;
komichi.show_message();
komichi.name();
komichi.address();
}
int main(int argc, char** argv) {
test01();
return 0;
}
(2)继承方式
class Person
{
public:
int m_a = 10;
protected:
int m_b = 20;
private:
int m_c = 30;
};
class son1:public Person
{
public:
void my_func()
{
cout << m_a << endl;
cout << m_b << endl;
//cout << m_c << endl;
}
};
class son2:protected Person
{
public:
void my_func()
{
cout << m_a << endl;
cout << m_b << endl;
//cout << m_c << endl;
}
};
class son3:private Person
{
public:
void my_func()
{
cout << m_a << endl;
cout << m_b << endl;
//cout << m_c << endl;
}
};
void test01()
{
son1 s;
s.my_func();
}
void test02()
{
son2 s;
s.my_func();
}
void test03()
{
son3 s;
s.my_func();
}
int main()
{
//test01();
//test02();
test03();
return 0;
}
(3)继承中的对象模型
class Father
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class Son:public Father
{
public:
int m_d;
};
void test01()
{
//16
//父类中所有非静态成员属性都会被子类继承下去
//父类中私有成员属性是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了
cout << sizeof(Son) <<endl;
}
int main()
{
test01();
return 0;
}
(4)继承中构造和析构顺序
class Father
{
public:
Father()
{
cout << "Father的构造函数" << endl;
}
~Father()
{
cout << "Father的析构函数" << endl;
}
};
class Son:public Father
{
public:
Son()
{
cout << "Son的构造函数" << endl;
}
~Son()
{
cout << "Son的析构函数" << endl;
}
};
void test01()
{
Son s1;
}
int main()
{
test01();
return 0;
}
(5)继承同名成员处理方式
class Father
{
public:
Father()
{
m_age = 100;
}
void func()
{
cout << "Father的func函数" << endl;
}
void func(int a)
{
cout << "Father的func(int a)函数" << endl;
}
int m_age;
};
class Son:public Father
{
public:
Son()
{
m_age = 200;
}
void func()
{
cout << "Son的func函数" << endl;
}
int m_age;
};
void test01()
{
Son s;
cout << s.m_age <<endl;//直接调用调用是子类中的同名成员
cout << s.Father::m_age << endl;
s.func();
//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数
//如果想访问到父类中被隐藏的同名成员函数,需要加作用域
s.Father::func(10);
s.Father::func();
}
int main()
{
test01();
return 0;
}
(6)继承同名静态成员处理方式
class Father
{
public:
static int m_age;
static void func()
{
cout << "Father的函数" << endl;
}
};
int Father::m_age = 100;
class Son:public Father
{
public:
static int m_age;
static void func()
{
cout << "Son的函数" << endl;
}
};
int Son::m_age = 200;
void test01()
{
Son s;
//1、通过对象访问
cout << "Son的m_age:" << s.m_age << endl;
cout << "Father的m_age:" << s.Father::m_age << endl;
//2、通过类名访问
cout << "Son的m_age:" << Son::m_age << endl;
//第一个::代表通过类名方式访问第二个::代表访问父类作用域下
cout << "Father的m_age:" << Son::Father::m_age << endl;
s.func();
//出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问
s.Father::func();
}
int main()
{
test01();
return 0;
}
(7)多继承语法
Son s;
sizeof(Son) <==> sizeof(s)
class Father1
{
public:
int m_a = 10;
};
class Father2
{
public:
int m_b = 20;
};
class Son:public Father1,public Father2
{
public:
int m_c = 30;
int m_d = 40;
};
void test01()
{
//当父类中出现同名成员,需要加作用域区分
Son s;
cout << "m_a:" << s.m_a << endl;
cout << "m_b:" << s.m_b << endl;
cout << "m_c:" << s.m_c << endl;
cout << "m_d:" << s.m_d << endl;
}
int main()
{
test01();
return 0;
}
(8)菱形继承
vbptr:虚基类指针
vbtable:虚基类表
class Animal
{
public:
int m_age = 20;
};
//利用虚继承解决菱形继承的问题
//继承之前加上关键字virtual变为虚继承
//Animal类称为虚基类
class Sheep :virtual public Animal
{
};
class Tuo :virtual public Animal
{
};
class SheepTuo : public Sheep,public Tuo
{
};
void test01()
{
SheepTuo st;
st.Sheep::m_age = 30;
st.Tuo::m_age = 10;
//当菱形继承,两个父类拥有相同数据,需要加以作用域区分
cout << "Sheep的age:" << st.Sheep::m_age << endl;
cout << "Tuo的age:" << st.Tuo::m_age << endl;
}
int main()
{
test01();
return 0;
}
18.多态
(1)多态的基本概念
在C++中,允许父子之间的类型转换(不需要做强制类型转换),父类的引用可以直接指向子类对象
//执行说话的函数
//地址早绑定在编译阶段确定函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定
//我们希望传入什么对象,那么就调用什么对象的函数
//如果函数地址在编译阶段就能确定,那么静态联编
//如果函数地址在运行阶段才能确定,就是动态联编
//动态多态满足条件
//1、有继承关系
//2、子类重写父类的虚函数
//动态多态使用
//父类的指针或者引用 指向子类对象
class Animal
{
public:
virtual void speak()
{
cout << "山里有各种各样的声音" << endl;
}
};
class Cat:public Animal//必须要有继承关系
{
public:
//重写函数返回值类型函数名参数列表完全相同
void speak()
{
cout << "这是哈基米" << endl;
}
};
class Show:public Animal
{
public:
void speak()
{
cout << "这是雪豹" << endl;
}
};
void doSpeak(Animal &animal)
{
animal.speak();
}
void test01()
{
Cat cat;
doSpeak(cat);
Show show;
doSpeak(show);
}
int main()
{
test01();
return 0;
}
类中定义的函数是分开存储的,内存上不属于类,类的大小为1,但加上virtual 后类的大小就变成4了,这4个字节为指针,类的内部多了一个虚函数表指针
(2)多态案例——计算器类
//如果想扩展新的功能,需求修改源码
//在真是开发中中提倡开闭原则
//开闭原则:对扩展进行开发,对修改进行关闭
//实现计算器抽象类
class AbstractComputer
{
public:
virtual int getResult()
{
return 0;
}
int m_a;
int m_b;
};
class Add:public AbstractComputer
{
public:
int getResult()
{
return m_a + m_b;
}
};
class Minus:public AbstractComputer
{
public:
int getResult()
{
return m_a - m_b;
}
};
class Mul:public AbstractComputer
{
public:
int getResult()
{
return m_a * m_b;
}
};
void test01()
{
//多态使用条件
//父类指针或者引用指向子类对象
AbstractComputer *ab;
ab = new Add;//该行代码就将虚函数进行了覆盖
ab->m_a = 10;
ab->m_b = 20;
cout << ab->m_a << "+" << ab->m_b << "=" << ab->getResult() << endl;
delete ab;//销毁堆区数据
ab = new Minus;//该行代码就将虚函数进行了覆盖
ab->m_a = 10;
ab->m_b = 20;
cout << ab->m_a << "-" << ab->m_b << "=" << ab->getResult() << endl;
delete ab;//销毁堆区数据
ab = new Mul;//该行代码就将虚函数进行了覆盖
ab->m_a = 10;
ab->m_b = 20;
cout << ab->m_a << "*" << ab->m_b << "=" << ab->getResult() << endl;
delete ab;//销毁堆区数据
}
int main()
{
test01();
return 0;
}
(3)纯虚函数和抽象类
抽象类无法在栈或堆上实例化对象
class Father
{
public:
virtual void func() = 0;
};
class Son1:public Father
{
public:
virtual void func()//2、抽象类的子类必须要重写父类中的纯虚函数,否则也属于抽象类
{
cout << "Son1的func()" << endl;
}
};
void test01()
{
Father *ab = new Son1;
ab->func();
}
int main()
{
test01();
return 0;
}
(4)多态案例二-制作饮品
防止堆区数据的内存泄漏,使用完堆区数据后要进行释放
class Abstract
{
public:
virtual void Boil() = 0;
virtual void Brew() = 0;
virtual void PourInCup() = 0;
virtual void plus() = 0;
void makedrink()
{
Boil();
Brew();
PourInCup();
plus();
}
};
class Coffee:public Abstract
{
public:
void Boil()
{
cout << "倒水" << endl;
};
void Brew()
{
cout << "冲泡咖啡" << endl;
};
void PourInCup()
{
cout << "倒入杯中" << endl;
};
void plus()
{
cout << "加入糖和牛奶" << endl;
};
};
class Tea:public Abstract
{
public:
void Boil()
{
cout << "倒水" << endl;
};
void Brew()
{
cout << "冲泡茶叶" << endl;
};
void PourInCup()
{
cout << "倒入杯中" << endl;
};
void plus()
{
cout << "加入枸杞" << endl;
};
};
void work(Abstract* ab)
{
ab->makedrink();
delete ab;
}
void test01()
{
work(new Coffee);
work(new Tea);
}
int main()
{
test01();
return 0;
}
(5)虚析构和纯虚析构
class Father
{
public:
Father()
{
cout << "Father的构造函数" << endl;
}
//实现虚析构或纯虚析构目的:解决父类指针无法访问子类析构,无法释放子类中的堆区数据
// //利用虚析构可以解决父类指针释放子类对象时不干净的问题
// virtual ~Father()
// {
// cout << "Father的析构函数" << endl;
// }
//虚析构和纯虚析构函数内部都需要一定的函数实现,不然父类内部可能开辟的堆区数据无法被释放
//纯虚析构需要声明也需要实现
//有了纯虚析构之后.,这个类也属于抽象类,无法实例化对象
virtual ~Father() = 0;
virtual void speak() = 0;
};
Father::~Father()
{
cout << "Father的纯虚析构函数" << endl;
}
class Son:public Father
{
public:
Son(string name)
{
m_name = new string(name);
cout << "Son的构造函数" << endl;
}
~Son()
{
cout << "Son的析构函数" << endl;
if(m_name != NULL)
{
delete m_name;
m_name = NULL;
}
}
void speak()
{
cout << *m_name << "是我涝坡" << endl;
}
string* m_name;
};
void test01()
{
Father * ab = new Son("Akebi");
ab->speak();
//父类指针在析构时候不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
delete ab;//需要对堆区数据进行释放
}
int main()
{
test01();
return 0;
}
(6)多态案例三-电脑组装
//自解
class CPU_Abstract
{
public:
virtual void factory() = 0;
virtual void work() = 0;
};
class Card_Abstract
{
public:
virtual void factory() = 0;
virtual void work() = 0;
};
class Memory_Abstract
{
public:
virtual void factory() = 0;
virtual void work() = 0;
};
class CPU_Intel:public CPU_Abstract
{
public:
void factory()
{
cout << "Intel工厂生产" << endl;
}
void work()
{
cout << "CPU烤肉" << endl;
}
};
class Card_huashuo:public Card_Abstract
{
public:
void factory()
{
cout << "华硕工厂生产" << endl;
}
void work()
{
cout << "显卡风扇开始转了" << endl;
}
};
class Memory_caihong:public Memory_Abstract
{
public:
void factory()
{
cout << "七彩虹工厂生产" << endl;
}
void work()
{
cout << "内存爆了" << endl;
}
};
class Computer
{
public:
void print_messages(CPU_Abstract* cpu,Card_Abstract* card,Memory_Abstract* memory)
{
cpu->factory();
card->factory();
memory->factory();
}
void processing(CPU_Abstract* cpu,Card_Abstract* card,Memory_Abstract* memory)
{
cpu->work();
card->work();
memory->work();
}
};
void test01()
{
Computer c;
c.print_messages(new CPU_Intel ,new Card_huashuo ,new Memory_caihong);
c.processing(new CPU_Intel ,new Card_huashuo ,new Memory_caihong);
}
int main()
{
test01();
return 0;
}
19.文件操作
(1)写文件
(2)读文件
void test01()
{
ofstream ofs;
ofs.open("test.txt" , ios::out);
ofs << "Akebi" << endl;
ofs << "Komichi" << endl;
ofs.close();
}
void test02()
{
ifstream ifs;
ifs.open("test.txt",ios::in);
if(!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
char data[1024];
//第一种
// while(ifs >> data)//当扫描并写入完后,返回False
// {
// cout << data << endl;
// }
//第二种
// while(ifs.getline(data,sizeof(data)))
// {
// cout << data << endl;
// }
//第三种
// string my_str;
// while(getline(ifs,my_str))//利用全局函数
// {
// cout << my_str << endl;
// }
//第四种//一个个读出字符
char c;
while((c = ifs.get()) != EOF)
{
cout << c ;
}
ifs.close();
}
int main(int argc, char** argv) {
//test01();
test02();
return 0;
}
(3)二进制文件
二进制写入文件可以写入自定义数据类型,比如class
class Person
{
public:
char name[1024];
int m_age;
};
void test01()
{
//2、创建流对象
//ofstreamn ofs("person.txt",ios::out | ios::binary);//可以在创建对象的时候,进行模式选择和文件名写入
ofstream ofs;
ofs.open("Person.txt",ios::binary | ios::out);//也可以使用write进行模式选择和文件名写入
Person p = {"Akebi Komichi" , 14};
// p.m_age = 14;
// p.name = "Akebi";
ofs.write((const char*)&p,sizeof(Person));
ofs.close();
}
int main()
{
test01();
return 0;
}
class Person
{
public:
char m_name[1024];
int m_age;
};
void test01()
{
ifstream ifs;
ifs.open("Person.txt" , ios::in | ios::binary);
Person p;
ifs.read((char*)&p , sizeof(Person));
if(!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
cout << p.m_age << endl;
cout << p.m_name << endl;
ifs.close();
}
int main()
{
test01();
return 0;
}
(4)职工管理系统
C++提高编程
一. 模板
(1)函数模板
主要目的是将类型参数化
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
//声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型
template<typename T>
void my_replace(T &a ,T &b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
//利用函数模板交换
//两种方式使用函数模板
//1、自动类型推导
// my_replace(a,b);
//2、显示指定类型
my_replace<int>(a,b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
(2)函数模板注意事项
(3)普通函数与函数模板的区别
(4)普通函数与函数模板的调用规则
1.函数模版和普通函数的参数,函数名等都 一致,那么调用普通函数
2.my_func<>(a ,b)中,“<>”就是空模版参数列表
4.如果参数传入char ,而普通函数需要int ,那么就不会发生隐式类型转换,而是模版直接自动类型推导
(5)模板的局限性
class Person
{
public:
Person(string name,int age)
{
this->m_age = age;
this->m_name = name;
}
string m_name;
int m_age;
};
template<typename T>
bool my_com(T &p1,T &p2)
{
if(p1 == p2)
{
return true;
}
else
{
return false;
}
}
template<> bool my_com(Person &p1,Person &p2)
{
if(p1.m_age == p2.m_age)
{
return true;
}
else
{
return false;
}
}
void test01()
{
Person p1("Akebi",14);
Person p2("Komichi",14);
if(my_com(p1,p2))
{
cout << "p1 == p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
}
int main()
{
test01();
return 0;
}
(6)类模板
template<class Nametype,class Agetype>
class Person
{
public:
Person(Nametype name, Agetype age)
{
this->m_age = age;
this->m_name = name;
}
void showinfo()
{
cout << "name:" << this->m_name << endl;
cout << "age:" << this->m_age << endl;
}
string m_name;
int m_age;
};
void test01()
{
Person<string,int> p1("Akebi" , 14);
p1.showinfo();
}
int main(int argc, char** argv) {
test01();
return 0;
}
(7)类模板与函数模板区别
//类模板在模板参数列表中可以有默认参数
template<class Nametype,class Agetype = int>
class Person
{
public:
Person(Nametype name, Agetype age)
{
this->m_age = age;
this->m_name = name;
}
void showinfo()
{
cout << "name:" << this->m_name << endl;
cout << "age:" << this->m_age << endl;
}
string m_name;
int m_age;
};
void test01()
{
Person<string> p1("Akebi" , 14);
p1.showinfo();
}
int main(int argc, char** argv) {
test01();
return 0;
}
(8)类模板中成员函数创建时机
template<class T>
class my_class
{
public:
T obj;
//类模版中的成员函数,是在调用的时候才进行创建
void my_func01()
{
obj.showPerson1();
}
void my_func02()
{
obj.showPerson2();
}
};
class Person1
{
public:
void showPerson1()
{
cout << "show Person1" << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "show Person2" << endl;
}
};
void test01()
{
my_class<Person2> p1;
// p1.my_func01();
p1.my_func02();
}
int main(int argc, char** argv) {
test01();
return 0;
}
(9)类模板对象做函数参数
template<class T1,class T2>
class Person
{
public:
Person(T1 name,T2 age)
{
this->m_age = age;
this->m_name = name;
}
void showPerson()
{
cout << "name: " << m_name << endl;
cout << "age: " << m_age << endl;
}
T1 m_name;
T2 m_age;
};
//类模板对象做函数参数
void my_print1(Person<string,int> &p)
{
p.showPerson();
}
//2、参数模板化
template<class T1,class T2>
void my_print2(Person<T1,T2> &p)
{
p.showPerson();
cout << "T1的类型:" << typeid(T1).name() << endl; //使用这个必须包含头文件#include <typeinfo>
cout << "T2的类型:" << typeid(T2).name() << endl;
}
//3、整个类模板化
template<class T>
void my_print3(T &p)
{
p.showPerson();
cout << "T的类型:" << typeid(T).name() << endl;
}
void test01()
{
Person<string,int>p1("Akebi",14);
my_print1(p1);
}
void test02()
{
Person<string,int>p1("Komichi",15);
my_print2(p1);
}
void test03()
{
Person<string,int>p1("Hobert",21);
my_print3(p1);
}
int main()
{
// test01();
test02();
// test03();
return 0;
}
(10)类模板分文件编写
类模版一开始是不会创建的,所以仅仅引用.h 文件不行,编译器无法得到.cpp文件中函数的具体实现
类模版的成员函数的创建时机是在调用的时候
//main.cpp
#include <iostream>
using namespace std;
#include "person.hpp"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void test01()
{
Person<string,int> P1("Akebi",14);
P1.showPerson();
}
int main(int argc, char** argv) {
test01();
return 0;
}
//person.hpp
template<class T1,class T2>
class Person
{
public:
Person(T1 name,T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<class T1,class T2>
void Person<T1,T2>::showPerson()
{
cout << "Name:" << this->m_Name << endl;
cout << "Age:" << this->m_Age << endl;
}