C++核心编程(黑马程序员视频个人总结)

C++核心编程

1.内存分区模型

1.1.1.四个区

代码区:存放函数体的二进制代码,由操作系统进行管理----共享、只读
全局区:存放全局变量、静态变量及常量,常量区中存放const修饰的全局变量和字符串常量,程序结束时由操作系统回收。
栈区:由编译器自动分配释放,存放函数的参数值、局部变量等
注意事项:不要返回局部变量的地址
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。在c++中主要利用new在堆区开辟内存。

int *p=new int(10);

//指针本质也是局部变量,放在栈上,指针保存的数据是放在堆区。

//指针本质也是局部变量,放在栈上,指针保存的数据是放在堆区。

1.1.2.new()运算符和delete运算符

(1)基本语法

int * func(){
	int *a=new int(10)return a;
	}
int main(){
	int *p=func();
	cout<<*p<<endl;
	delete p;
	int *arr=new int[10];//返回新开辟的数组地址
	for(int i=0;i<10;i++){
		arr[i]=i+100;
	}
	for(int i=0;i<10;i++){
		cout<<arr[i]<<endl;
	}
    delete arr[];
	system("pause");
	return 0;
}

2.引用

2.2.1

//引用必须初始化,初始化后不可以被改变
语法:数据类型 &别名=原名;

int a=10;
int &b=a;
2.2.2.引用做函数参数

优点:可以简化指针修改实参

void swap03(int& a, int& b)
{
	int temp = a;
		a = b;
		b = temp;
		cout << "a=" << a << endl;
		cout << "b=" << b << endl;
}
int main()
{
	int a = 10;
	int b = 20;
	swap03(a, b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	system("pause");
	return 0;
}
2.2.3引用做函数返回值

注意:不要返回局部变量的引用

//返回局部变量引用
int & test01(){
	int a=10;//局部变量
	return a;
	}
//返回静态变量引用
int& test02(){
	static int a=10;//静态变量
	return a;
}
int main(){
	int &ref=test02();
	cout<<ref<<endl;//输出10
	test02()=1000;//函数调用可以作为左值,如果函数的返回值是一个引用,这个函数调用可以作为左值
	cout<<ref<<endl;//输出1000
	}
2.2.4引用本质

引用本质是一个指针常量。引用一旦初始化后就不可以被更改。

int a=10;

int &b=a;  //发现是引用,自动转换为 int * const b=&a;
2.2.5常量引用

使用场景:用来修饰形参,防止误操作

const int &ref=10;//编译器将代码修改为  int temp=10;const int &ref=temp;
//ref=20;//错误,加入const之后变为只读,不可以修改

3.函数提高

3.3.1函数默认参数

(1)函数声明由默认参数,实现就不能有默认参数,声明和实现只能有一个有默认参数。

(2)若从某个参数位置起由默认参数,则从这个参数起的右边都要有默认参数。

(3)有默认参数时,优先使用传递的默认参数。

3.3.2函数重载

可以让函数名相同,提高函数复用性。

函数重载必须满足条件:
(1)同一个作用域下

(2)函数名称相同

(3)函数参数 类型不同或者个数不同或者顺序不同

注意事项:函数的返回值不可以作为函数重载的条件。

3.3.3.函数重载注意事项

(1)引用作为重载的条件

void func(int &a)
{
	cout<<"func(int &a)的调用"<<endl;
}
void func(const int &a)
{
	cout<<"func(const int &a)的调用"<<endl;
}
int main()
{
	int a=10;
	func(a);//调用 func(int &a) 因为a可读可写
    //func(10);调用 func(const int &a) const限制只读状态
	system("pause");
	return 0;
}

(2)函数重载碰到默认参数

void func2(int a,int b=10)
{
	cout<<"func2(int a,int b)的调用"<<endl;
}
void func2(int a)
{
	cout<<"func2(int a)的调用"<<endl;
}
int main()
{
	int a=10;
	//func2(); 出错,当函数重载碰到默认参数,出现二义性
	system("pause");
	return 0;
}

4.类和对象

c++面向对象的三大特征**:继承、封装、多态**

4.1封装

4.1.1.封装的访问权限

公共权限 public 成员类内可以访问 类外可以访问

保护权限 protected 成员类内可以访问 类外不可以访问 子类可以访问父类中的保护内容

私有权限 private 成员类内可以访问 类外不可以访问 子类不可以访问父类中的保护内容

4.1.2.struct和class的区别

struct默认权限为公共。

class默认权限为私有。

4.1.3.成员属性设置为私有

(1)可以自己控制读写的权限

(2)对于写可以检测数据的有效性

4.1.4.封装案例

点和圆的关系判断。

#include<iostream>
#include<string>
using namespace std;
class Point
{
private:
	int m_X;
	int m_Y;
public:
	void setX(int x)
	{
		m_X = x;
	}
	int getX()
	{
		return m_X;
	}
	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()
{
	Point p1,p2;
	p1.setX(10);
	p1.setY(0);
	p2.setX(10);
	p2.setY(12);
	Circle c1;
	c1.setR(10);
	c1.setCenter(p1);
	isInCircle(c1, p2);
	system("pause");
	return 0;
}
4.1.5.构造函数和析构函数

构造函数:主要作用在于创建对象时为对象的成员赋值,构造函数由编译器自动调用,无需手动调用。

析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

4.1.6.构造函数

按参数类型分:有参构造和无参构造(默认构造)

按类型分:普通构造和拷贝构造

若定义有参构造,则c++不提供默认无参构造,但是提供默认拷贝构造函数。

若定义拷贝构造函数,系统不会在提供其他构造函数。

#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
	Person()
	{
		cout << "Person的无参构造函数调用!" << endl;
	}
	Person(int a,int height)
	{
		age = a;
		m_height =new int(height);
		cout << "Person的有参构造函数调用!" << endl;
	}
	Person(const Person& p)//拷贝构造函数
	{
		age = p.age;
		m_height =new int(*p.m_height);
	}
	~Person()
	{
		if(m_height !=NULL)
		{ 
			delete m_height;
			m_height = NULL;
		}
		cout << "Person的析构函数调用!" << endl;
	}
	int age;
	int *m_height;
};
void test01()
{
	//括号法
	/*Person p1;
	Person p2(10);
	Person p3(p2);
	cout << p2.age<< endl;*/
	//显示法
	Person p1;
	Person p2 = Person(10,160);
	Person p3 = Person(p2);
    //Person(10);  匿名对象  特点:当前行执行结束后,系统会立即回收掉匿名对象
    //不要利于拷贝构造函数初始化匿名对象
    //隐式转换法 
    Person p4=10;//相当于写了Person p4=Person(10);
    Person p5=p4;  //拷贝构造
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.1.7.深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

#include <iostream>
using namespace std;
//浅拷贝带来的问题就是堆区的内存重复释放,用深拷贝来解决
class Person 
{
public:
	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}
	Person(int age,int height)
	{
		m_Age = age;
		m_Height = new int(height);
		cout << "Person的有参构造函数调用" << endl;
	}
	int m_Age;
	int *m_Height;
	//
	Person(const Person &p)
	{
		cout << "Person的拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
		//m_Height = p.m_Height;   编译器默认实现代码就是这行代码
		m_Height = new int(*p.m_Height);//深拷贝 重新在堆区开辟内存
	}
	~Person()
	{
		if (m_Height != NULL)
		{
			delete m_Height;
			m_Height = NULL;
		}
		cout << "Person的析构函数调用" << endl;
	}
};
void test01()
{
	Person p1(18,190);
	cout << "p1的年龄为" << p1.m_Age << "身高为:"<<*p1.m_Height<<endl;
	Person p2(p1);
	cout << "p2的年龄为" << p2.m_Age << "身高为:" << *p2.m_Height << endl;

}
int main()
{
	test01();
	system("pause");
	return 0;
}
4.1.8.初始化列表和类对象作为类成员
//当其他类作为本类成员时:构造时候先构造类对象,在构造自身,析构顺序与构造相反
class Phone
{
public:
	string pName;
	Phone(string name)
	{
		pName = name;
		cout << "Phone的构造函数!" << endl;
	}
	~Phone()
	{
		cout << "Phone的析构函数" << endl;
	}
};
class Person
{
public:
	string m_name;
	Phone m_phone;
	Person(string name, string phone) :m_name(name), m_phone(phone)
	{
		cout << "Person的构造函数!" << endl;
	}
	~Person()
	{
		cout << "Person的析构函数" << endl;
	}
	static void func()
	{
		c = 0;
		cout << "Person静态函数!" << endl;
	}
	static int c;
	int b;
   private://静态成员函数由访问权限
    void func2()
    {
        cout<<"func2()的调用"<<endl;
    }
};
int Person::c= 10;
void test01()
{
Person p("张三", "苹果");
Person::func();//通过类名访问
//Person::func2();//不可以被访问
p.func(); //通过对象访问静态成员函数
}
int main()
{
	test01();
	system("pause");
	return 0;
}
4.1.9.静态成员函数

代码见上一点。

特点:(1)所有对象共享同一个函数

​ (2)静态成员函数只能访问静态成员变量

4.1.10.C++对象模型

成员变量和成员函数分开存储

空对象占用内存空间为1 。C++编译器为每个空对象也分配一个内存空间。

  • 非静态成员变量属于类的对象上。
  • 静态成员变量不属于类的对象上。
  • 非静态成员函数不属于类的对象上。
  • 非静态成员函数不属于类的对象上。
4.1.11.this指针

this指针指向被调用的成员函数所属的对象。

用途:

  • 当形参和变量同名时,可以this指针来区分。
  • 在类的非静态成员函数中返回对象本身,可使用return *this;
#include<iostream>
#include<string>
using namespace std;
//
class Person
{
public:
	Person(int age)
	{
		this->age = age;//this指针指向被调用的成员函数所属的对象。
	}
	Person&  addPerson(Person& p)//返回对象本身用引用
	{
		this->age += p.age;
		return *this;
	}
	int age;
};
//1.解决名称冲突
void test01()
{
	Person p1(18);
	cout << "p1的年龄为" << p1.age << endl;
}
//2.返回对象本身用this
void test02()
{
	Person p1(10);
	Person p2(10);
    //链式编程思想
	p2.addPerson(p1).addPerson(p1);
	cout << "p2的年龄为" << p2.age << endl;
}

int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

4.1.12.const修饰成员对象

常函数:

  • 成员函数加const后我们称这个函数为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改
  • 常对象:
  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
	//this本质是指针常量,指针指向不可以修改,
	//const Person *const this
	//常函数不能修改成员属性
	void shoePerson()const//在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
	{
		this->m_B = 100;
	}
	int m_A;
	mutable int m_B;//特殊变量,在常函数中也可以修改这个值
};
void test()
{
	const Person p;//常对象
	p.m_B = 10;//特殊值,常对象下也可以修改
	//常对象只能调用常函数
	p.shoePerson();
	//p.func();
}
int main()
{

	system("pause");
	return 0;
}
4.1.13.友员
  • 全局函数做友员
  • 类做友员
  • 成员函数做友员
#include<iostream>
#include<string>
using namespace std;
class Building;
class GoodGay
{
public:
	GoodGay();
	Building* building;
	void visit();//可以访问Building中的私有属性//friend void GoodGay::visit();
	void visit2();//不可以访问Building中的私有属性
};
class Building
{
	friend class GoodGay;//类做友员
	friend void goodGay(Building* building);//全局函数做友员
	friend void GoodGay::visit();
public:
	string m_SittingRoom;
	/*Building()
	{
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}*/
	Building();
private:
	string m_BedRoom;

};
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
	building = new Building;
}
void GoodGay::visit()
{
	cout << "好基友类 正在访问:" << building->m_SittingRoom << endl;
	cout << "好基友lei正在访问:" << building->m_BedRoom << endl;
}
void GoodGay::visit2()
{
	cout << "好基友类 正在访问:" << building->m_SittingRoom << endl;
	cout << "好基友lei正在访问:" << building->m_BedRoom << endl;
}
//void goodGay(Building* building)
//{
//	cout << "好基友的全局函数 正在访问:" << building->m_SittingRoom << endl;
//	cout << "好基友的全局函数 正在访问:" << building->m_BedRoom << endl;
//
//}
//void test01()
//{
//	Building building;
//	goodGay(&building);
//}
void test02()
{
	GoodGay gg;
	gg.visit();

}
int main()
{
	test02();
	system("pause");
	return 0;
}

4.2运算符重载

注意:

  • 对于内置数据类型的表达式的运算符式不可能改变的
  • 不要滥用运算符重载
4.2.1.加号运算符重载
#include<iostream>
#include<string>
using namespace std;
//加号运算符重载
class Person
{
public:
	int m_A;
	int m_B;
	//1.成员函数重载  本质 p3=p1.operator(p2)
	Person operator+(Person & p)
	{
		Person temp;
		temp.m_A = this->m_A + p.m_A;
		temp.m_B= this->m_B + p.m_B;
		return temp;
	}
private:

};
//2.全局函数重载  p3=operator+(p1,p2)
Person operator+(Person & p1, Person & p2)
{
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}
//重载
Person operator+(Person& p1, int number)
{
	Person temp;
	temp.m_A = p1.m_A + number;
	temp.m_B = p1.m_B + number;
	return temp;
}
void test01()
{
	Person p1, p2;
	p1.m_A = 10;
	p1.m_B = 10;
	p2.m_A = 10;
	p2.m_B = 10;
	Person p3;
	p3 = p1 + p2;
	cout << "p3.m_A=" << p3.m_A << endl;
	cout << "p3.m_B=" << p3.m_B << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}
4.4.2.左移运算符重载

总结:重载左移运算符配合友元可以实现输出自定义数据类型

#include <iostream>
using namespace std;
class Person
{
    friend ostream operator<<(ostream &cout,Person &p) ;//利用友员函数访问私有属性
    public:
    	Person(int a,int b)
        {
            m_A=a;
            m_B=b;
        }
    private:
    	int m_A;
    	int m_B;
	//利用成员函数重载左移运算符  p.opertor<<(cout) 简化版本 <<cout 一般不用成员函数重载左移运算符
    /*Person operator<<(cout)
    {
	}*/
}
//利用全局函数重载运算符
ostream& operator<<(ostream &cout,Person &p) //本质 operaror<<(cout,p) 简化 cout<<p;
{
    cout<<"m_A="<<p.m_A<<" m_B="<<p.m_B;
    return cout;//这样可以继续做链式编程 返回输出流对象cout
}
void test01()
{
    Person p(10,10);
    cout<<p;
}
int main()
{
    test01();
    system("pause");
    return 0;
}
4.2.3.递增运算符重载

前置递增返回引用,后置递增返回值。

#include <iostream>
using namespace std;
class MyInteger
{
    friend ostream& operater<<(ostream &cout,MyInteger &myInt);
public:
    MyInteger()
    {
        m_Num=0;
    }
    //重载++运算符
    //重载前置++运算符
    MyInteger& operator++()     //返回引用是为了对一个对象进行操作
    {
        m_Num++;//先进行++
        return *this;//返回自身
    }
    //重载后置运算符 返回值
    MyInteger operate++(int)  //int 代表一个占位参数 可以用于区分前置和后置递增
    {
        //先 记录当时结果
        MyInteger temp=*this;
        //后 递增
        m_Num++;
        //最后将记录结果做返回
        return temp;
        
    }
    private:
    	int m_Num;
}
ostream& operater<<(ostream &cout,MyInteger &myInt)
{
    cout<<myInt.m_NUm;
    return cout;
}
void test01()
{
    MyInteger myInt;
    cout<<++myInt<<endl;
}
void test02()
{
    MyInteger myInt;
    cout<<++myInt<<endl;
    cout<<myInt<<endl;
}
int main()
{
    test01();
    system("pause");
    return 0;
}
4.2.4.递减运算符
#include<iostream>
#include<string>
using namespace std;
class MyInteger
{
	friend ostream& operator<<(ostream& cout, const MyInteger myint);
public :
	int m_Num;
	MyInteger()
	{
		m_Num = 3;
	}
	//重载前置递减运算符
	/*MyInteger& operator--()
	{
		m_Num--;
		return *this;
	}*/
	//重载后置递减运算符
	MyInteger operator--(int)
	{
		MyInteger temp=*this;
		m_Num--;
		return temp;
	}
};
ostream& operator<<(ostream& cout, const MyInteger myint)
{
	cout << myint.m_Num;
	return cout;
}
//void test01()
//{
//	MyInteger myint;
//	cout <<--myint << endl;
//}
void test02()
{
	MyInteger myint;
	cout << myint-- << endl;
	cout << myint << endl;
}
int main()
{
	test02();
	system("pause");
	return 0;
}
4.2.5.赋值运算符重载
#include <iostream>
using namespace std;
class Person
{
public:
	Person(int age)
    {
     	m_Age=new int(age);   
    }
    //重载赋值运算符
    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;
    }
    ~Person()
    {
        if(m_Age!=NULL)
        {
            delete m_Age;
            m_Age=NULL;
        }
    }
	int *m_Age;
}
void test01()
{
    Person p1(18);
    Person p2(20);
    Person p3(10);
    p3=p2=p1;
    cout<<"p1的年龄为:"<<*p1.m_Age<<endl;
    cout<<"p2的年龄为:"<<*p2.m_Age<<endl;
    cout<<"p2的年龄为:"<<*p3.m_Age<<endl;
}
4.2.6.关系运算符重载

作用:可以让两个自定义类型对象进行对比操作

#include <iostream>
using namespace std;
class Person
{
    public:
    Person(string name,int age)
    {
        m_Name=name;
        m_Age=age;
    }
    //重载==运算符
    bool operator==(Person &p)
    {
        if(this->m_Name==p.m_Name&&this->m_Age==p.m_Age)
            return ture;
        return false;
    }
    bool operator!=(Person &p)
    {
        if(this->m_Name!=p.m_Name||this->m_Age!=p.m_Age)
            return ture;
        return false;
    }
    string m_Name;
    int m_Age;
}
void test01()
{
    Person p1("tom",18);
    Person p2("Amy",18);
    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();
    system("pause");
    return 0;
}
4.2.7.函数调用运算符重载
  • 函数调用运算符()也可以重载
  • 由于重载后的使用方式非常像函数的调用,因此成为仿函数
  • 仿函数没有固定写法,非常灵活
#include<iostream>
#include <string>
using namespace std;
class MyPrint
{
public:
	void operator()(string test)
	{
		cout << test << endl;
	}
};
void test01()
{
	MyPrint myPrint;
	myPrint("Hello world!")// 由于使用起来非常类似函数调用。所以叫仿函数
}
class MyAdd
{
    public:
		int operator()(int num1,int num2)
        {
            return num1+num2;
        }
}
void test02()
{
    MyAdd myadd;
    int ret=myadd(10,10);
    cout<<"ret="<<ret<<endl;
    //匿名函数对象
    cout<<MyAdd()(100,100)<<endl;
}
int main()
{
	test01();
    test02();
	system("pause");
	return 0;
}

4.3继承

4.3.1.继承的方式
  • 公共继承 class A :public B
  • 保护继承 class A :protected B
  • 私有继承 class A :private B

注意:

  • 父类中所以非静态成员属性都被子类继承
  • 父类中私有成员属性,是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gzOqPziO-1602227949138)(C:\Users\leovo\AppData\Roaming\Typora\typora-user-images\image-20201005145350753.png)]

4.3.2.Visual Studio 开发人员命令提示符

1.找到该项目路径

跳转盘符 E:

跳转文件路径 cd+路径

dir 查看目录

查看命令 cl /d1 reportSingleClassLayout类名 文件名 报告单个类布局

4.3.3.继承中构造和析构的顺序

先构造父类,在构造子类;先析构子类,在析构父类。

4.3.4继承同名成员处理方式
  • 访问子类同名成员 直接访问
  • 访问父类同名成员 需要加作用域
  • 如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数
  • 如果想访问到父类中被隐藏的同名成员函数,需要加作用域
4.3.5.继承同名静态成员处理方式
  • 访问子类同名成员 直接访问
  • 访问父类同名成员 需要加作用域
  • 如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数
#include<iostream>
#include<string>
using namespace std;
class Base
{
public:
	static void func()
    {
        cout<<"Base的静态成员函数"<<endl;
    }
	static int m_A;//编译阶段分配内存 类内声明,类外实现 
    
};
int Base::m_A=100;
class Son::public Base
{
    public:
    static void func()
    {
        cout<<"Son的静态成员函数"<<endl;
    }
    	sttic int m_A;
}
int Son::m_A=200;
//同名静态成员属性
void test01()
{
    //1.通过对象访问
    Son s;
    cout<<"Son 下 m_A="<<s.m_A<<endl;//结果为200
    cout<<"Base 下 m_A="<<s.Base::m_A<<endl;//结果为100
    //2.通过类名访问
    cout<<"通过类名访问:"<<endl;
    cout<<"Son 下 m_A="<<Son::m_A<<endl;//结果为200
    //第一个::代表通过类名方式访问 第二个代表访问父类作用域下的
    cout<<"Base 下 m_A="<<Son::Base::m_A<<endl;//结果为100
}
//同名静态成员函数
void test02()
{
     //1.通过对象访问
    Son s;
    s.func();
    s.Base::func();
    //2.通过类名访问
    Son::func();
    Son::Base::func():
}
int main()
{
    test01();
    test02();
    system("pause");
    return 0;
}
4.3.6多继承语法

C++允许一个类继承多个类。

#include<iostream>
#include<string>
using namespace std;
class Base1
{
public:
	Base1()
	{
		m_A = 100;
	}
	int m_A;
};
class Base2
{
public:
	Base2()
	{
		m_B = 100;
		m_A = 200;
	}
	int m_A;
	int m_B;
};
class Son :public Base1, public Base2
{
public:
	Son()
	{
		m_C = 300;
		m_D = 400;
	}
	int m_C;
	int m_D;
};
void test01()
{
	Son s;
	cout << "Son的内存空间"<<sizeof(s) << endl;
	//当父类中出现同名成员,需要加作用域
	cout << "s.Base1::m_A=" << s.Base1::m_A << endl;
	cout << "s.Base2::m_A=" << s.Base2::m_A << endl;

}
int main()
{
	test01();
	system("pause");
	return 0;
}
4.3.7.菱形继承

菱形继承概念:

  • 两个派生类继承同一个基类

  • 又有某个类同时继承两个派生类

  • 这种继承称为菱形继承,或者砖石继承。

菱形继承问题:

  • 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
  • 草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
#include<iostream>
#include<string>
using namespace std;
//利用虚继承可以解决菱形继承的问题 
//继承之前加上virtual 虚继承
//Animal 类成为虚基类  虚基类表和虚基类指针(偏移量) 
class Animal
{
public:
	
	int m_Age;
};
class Sheep :virtual public Animal
{
public:
	Sheep()
	{
	}
};
class Tuo :virtual public Animal
{
public:
	Tuo()
	{
	}	
};
class SheepTuo :public Sheep, public Tuo
{

};
void test01()
{
	SheepTuo st;
	st.Sheep::m_Age = 18;
	st.Tuo::m_Age = 28;

	cout << "st.Sheep::m_Age" << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age=" << st.Tuo::m_Age << endl;
    //这个数据我们知道有一份就可以,菱形继承导致有两份,资源浪费
	cout << "st.m_Age=" << st.m_Age << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.4多态

4.4.1多态的基本概念

多态分为两类

  • 静态多态:函数重载和运算符重载属于静态多态,复用函数名
  • 动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别:

  • 静态多态的函数地址早绑定–编译阶段确定函数地址
  • 动态多态的函数地址晚绑定·运行阶段确定函数地址

动态多态条件:
1.有继承关系
2.子类重写父类的虚函数 //重写 函数返回值类型 函数名 参数列表 完全相同
动态多态使用:
父类的指针或者引用 指向 子类对象

#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
    //虚函数
	virtual void speak()
	{
		cout << "动物在说话!" << endl;
	}
};
class Cat :public Animal
{
public:
	void speak()
	{
		cout << "小猫在说话!" << endl;

	}
};
class Dog :public Animal
{
public:
	void speak()  //virtual 可写可不写
	{
		cout << "小狗在说话!" << endl;

	}
};
//地址早绑定 在编译阶段确定函数地址
//如果想执行猫说话,函数的地址不能早绑定,要晚绑定
void doSpeak(Animal &animal)  //Animal &animal = cat;
{
	animal.speak();
}
void test01()
{
	Cat cat;
	Dog dog;
	doSpeak(cat);
	doSpeak(dog);
}
int main()
{
	test01();
	system("pause");
	return 0;
}
4.4.2.多态的原理剖析

虚函数指针 虚函数表

4.4.3 多态-案例1-计算器类
#include<iostream>
#include<string>
using namespace std;
//分别利于普通写法和多态实现计算器
class Caculator
{
public:
	int getResult(string oper)
	{
		if (oper == "+")
		{
			return m_Num1 + m_Num2;
		}
		else if (oper == "-")
		{
			return m_Num1 - m_Num2;
		}
		else if (oper == "*")
		{
			return m_Num1 * m_Num2;
		}
		else if (oper == "/")
		{
			return m_Num1 / m_Num2;
		}

	}
	
	int m_Num1;
	int m_Num2;
};
void test01()
{
	//创建对象
	Caculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 10;
	cout << "m_Num1+m_Num2=" << c.getResult("+") << endl;
}
//利用多态实现
class AbstractCaculator
{
public:
	virtual int getResult()
	{
		return 0;
	}
	int m_Num1;
	int m_Num2;
};
class AddCaculator :public AbstractCaculator
{
public:
	int getResult()
	{
		return m_Num1 + m_Num2;
	}
};
class SubCaculator :public AbstractCaculator
{
public:
	int getResult()
	{
		return m_Num1 - m_Num2;
	}
};
class MulCaculator :public AbstractCaculator
{
public:
	int getResult()
	{
		return m_Num1 * m_Num2;
	}
};
void test02()
{
	//实现加法
	AbstractCaculator* abc = new AddCaculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;//用完记得销毁
    
	abc = new SubCaculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;

	delete abc;//用完记得销毁
	abc = new MulCaculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;

}
int main()
{

	test02();
	system("pause");
	return 0;
}
4.4.4 纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容

因此可以将虚函数改为纯虚函数。

纯虚函数语法:

virtual 返回值类型 函数名 (参数列表)= 0;

当类中有了纯虚函数,这个类也称为抽象类。

抽象类特点:

  • 无法实例化对象
  • 子类必须重写父类中的纯虚函数,否则也属于抽象类。
#include <iostream>
#include <string>
using namespace std;
//只要有一个纯虚函数,这个类成为抽象类
class Base{
public:
	virtual void func() = 0;//纯虚函数 
};
class Son :public Base 
{
public:
	virtual void func()
	{
		cout << "fdkifkk!" << endl;
	}
};
void test01()
{
	Base* abc = new Son;
	abc->func();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4.4.5多态-案例2-制作饮品

#include <iostream>
#include <string>
using namespace std;
class  AbstractDrinking
{
public:
	virtual void Boil() = 0;//煮水
	virtual void Brew() = 0;//冲泡
	//PourIncup
	virtual void PourIncup() = 0;
	//辅料
	virtual void PutSomething() = 0;
	void makeDrink()
	{
		Boil();
		Brew();
		PourIncup();
		PutSomething();
	}
};
class  Coffee:public AbstractDrinking
{
public:
	virtual void Boil()
	{
		cout << "煮农夫山泉!" << endl;
	}//煮水
	virtual void Brew()
	{
		cout << "冲泡!" << endl;
	};//冲泡
	//PourIncup
	virtual void PourIncup()
	{
		cout << "倒入杯中!" << endl;
	}
	//辅料
	virtual void PutSomething()
	{
		cout << "加入牛奶和糖!" << endl;
	}
	
};
class  Tea :public AbstractDrinking
{
public:
	virtual void Boil()
	{
		cout << "煮矿泉水!" << endl;
	}//煮水
	virtual void Brew()
	{
		cout << "冲泡茶叶!" << endl;
	};//冲泡
	//PourIncup
	virtual void PourIncup()
	{
		cout << "倒入杯中!" << endl;
	}
	//辅料
	virtual void PutSomething()
	{
		cout << "加入柠檬!" << endl;
	}

};
void doWork(AbstractDrinking* abs)
{
	abs->makeDrink();
	delete abs;//手动释放
}
void test01()
{
    //制作咖啡
	doWork(new Coffee);
    //制作茶叶
	doWork(new Tea);
}
int main()
{
	test01();
	system("pause");
	return 0;
}
4.4.5虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方式: 将父类中的析构函数改为虚析构或者纯虚析构

若子类中没有堆区数据,可以不写虚析构或者纯虚析构

虚析构和纯虚析构共性:

  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现

虚析构和纯虚析构区别:

  • 如果是纯虚析构,该类属于抽象类,无法实例化对象
#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
	Animal()
	{
		cout << "Animal构造函数" << endl;

	}
	/*virtual~Animal()           虚析构
	{
		cout << "Animal析构函数" << endl;

	}*/
    //因为子类中有开辟在堆区的数据,释放不干净,所以写虚析构
	virtual~Animal() = 0;// 纯虚析构 无法实例化对象  需要声明也需要代码实现
    
	virtual void speak()
	{
		cout << "动物在说话!" << endl;
	}
};
Animal::~Animal()//纯虚析构实现代码
{
	cout << "Animal纯虚析构函数" << endl;

}
class Cat :public Animal
{
public:
	Cat(string name)
	{
		cout << "cat构造函数" << endl;
		m_Name = new string("Tom");
	}
	 virtual void speak()
	{
		cout <<*m_Name<< "小猫在说话!" << endl;

	}
	string* m_Name;
	~Cat()
	{
		cout << "Cat析构函数" << endl;
		if (m_Name != NULL)
		{
			delete m_Name;
			m_Name = NULL;
		}
	}
};
class Dog :public Animal
{
public:
	virtual void speak()
	{
		cout << "小狗在说话!" << endl;
	}
};
void test01()
{
	Animal * animal= new Cat("Tom");
	animal->speak();
	delete animal;
}
int main()
{
	test01();
	system("pause");
	return 0;
}
4.4.6多态-案例3-电脑组装
#include<iostream>
#include<string>
using namespace std;  
class CPU
{
public:
	virtual void caculate() = 0;
};
class VideoCard
{
public:
	virtual void display() = 0;
};
class Memory
{
public:
	virtual void storage() = 0;
};
class  Computer
{
public:
	Computer(CPU* cpu,VideoCard* vc,Memory* mem)
	{
		m_cpu = cpu;
		m_vc = vc;
		m_mem = mem;
	}
	void work()
	{
		m_cpu->caculate();
		m_vc->display();
		m_mem->storage();
	}
	~Computer()
	{
		if (m_cpu != NULL)
		{
			delete m_cpu;
			m_cpu = NULL;
		}
		if (m_vc != NULL)
		{
			delete m_vc;
			m_vc = NULL;
		}
		if (m_mem != NULL)
		{
			delete m_mem;
			m_mem = NULL;
		}
	}
private:
	CPU*  m_cpu;
	VideoCard* m_vc;
	Memory* m_mem;
};
class IntelCpu:public CPU
{
public:
	void caculate()
	{
		cout << "Intel 的CPU开始工作了!" << endl;
	}
};
class IntelVideoCard :public VideoCard
{
public:
	void display()
	{
		cout << "Intel 的显卡开始工作了!" << endl;
	}
};
class IntelMemory :public Memory
{
public:
	void storage()
	{
		cout << "Intel 的内存条开始工作了!" << endl;
	}
};
class LenovoCpu :public CPU
{
public:
	void caculate()
	{
		cout << "Lenovo 的CPU开始工作了!" << endl;
	}
};
class LenovoVideoCard :public VideoCard
{
public:
	void display()
	{
		cout << "Lenovo 的显卡开始工作了!" << endl;
	}
};
class LenovoMemory :public Memory
{
public:
	void storage()
	{
		cout << "Lenovo 的内存条开始工作了!" << endl;
	}
};
void test01()
{
	//第一台
	CPU* intelCpu = new IntelCpu;
	VideoCard* intelVideoCard = new IntelVideoCard;
	Memory* intelMemory = new IntelMemory;
	Computer* computer1 = new Computer(intelCpu, intelVideoCard, intelMemory);
	computer1->work();
	delete computer1;

}
void test02()
{
	//第2台
	CPU* lenovoCpu = new LenovoCpu;
	VideoCard* lenovoVideoCard = new LenovoVideoCard;
	Memory* lenovoMemory = new LenovoMemory;
	Computer* computer2 = new Computer(lenovoCpu, lenovoVideoCard, lenovoMemory);
	computer2->work();
	delete computer2;

}
void test03()
{
	//第3台
	CPU* intelCpu = new IntelCpu;
	VideoCard* lenovoVideoCard = new LenovoVideoCard;
	Memory* intelMemory = new IntelMemory;
	Computer* computer3 = new Computer(intelCpu, lenovoVideoCard, intelMemory);
	computer3->work();
	delete computer3;

}
int main()
{
	test01();
	test02();
	test03();
	system("pause");
	return 0;
}

5.文件操作

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
C++中对文件操作需要包含头文件**< fstream >**
文件类型分为两种:
1.文本文件 -文件以文本的ASCII码形式存储在计算机中
2.二进制文件-文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们

操作文件的三大类:

  1. ofstream:写操作
  2. ifstream:读操作
  3. fstream:读写操作

5.1文本文件

5.1.1写文件

写文件步骤如下:
1.包含头文件
#include
2.创建流对象
ofstream ofs;
3.打开文件
ofs.open(“文件路径”,打开方式);
4.写数据
ofs<< “写入的数据”;
5.关闭文件
ofs.close();

文件打开方式:
打开方式

打开方式解释
ios::in为读文件而打开文件
ios::out为写文件而打开文件
ios::ate初始位置:文件尾
ios::app追加方式写文件
ios::trunc如果文件存在先删除,再创建
ios:binary二进制方式

注意:文件打开方式可以配合使用,利用|操作符

例如:用二进制方式写文件

ios::binary| ios::out
#include <iostream>
#include<fstream>
using namespace std;
void test01()
{
	//1.包含头文件
	
	//2.创建流对象
	ofstream ofs;
	//3.打开文件
	ofs.open("test.txt",ios::out);
	//4.写数据
	ofs<<"姓名:张三"<<endl;
	ofs<<"年龄:18"<<endl;
	//5.关闭文件
	ofs.close();
}
int main()
{
	test01();
	system("pause");
	return 0;
}
5.1.2读文件

读文件步骤如下:
1.包含头文件
#include
2.创建流对象
ifstream ifs;
3.打开文件并判断文件是否打开成功
ifs.open(“文件路径” ,打开方式);
4.读数据
四种方式读取
5.关闭文件
ifs.close();

#include <iostream>
#include<fstream>
#include<string>
using namespace std;
void test01()
{
	//1.包含头文件
	//#include <fstream>
	//2.创建流对象
	ifstream ifs;
	//3.打开文件并判断文件是否打开成功
	ifs.open("test.txt" ,ios::in);
    if(!ifs.is_open())
    {
        cout<<"文件打开失败"<<endl;
        return;
    }
	//4.读数据  四种方式读取
    //第一种
    char buf[1024]={0};
    while(ifs>>buf)
    {
        cout<<buf<<endl;
    }
    //第二种
    char buf[1024]={0};
    while(ifs.getline(buf,sizeof(buf)))
    {
		cout<<buf<<endl;
    }
    //第三种
    string buf;
    while(getline(ifs,buf))
    {
		cout<<buf<<endl;
    }
    //第四种
    char c;
    while((c=ifs.get()!=EOF)  //End Of File
    {
		cout<<c;
    }
	//5.关闭文件
	ifs.close();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

5.2二进制文件

以二进制的方式对文件进行读写操作
打开方式要指定为ios::binary

5.2.1写文件

二进制方式写文件主要利用流对象调用成员函数write
函数原型:

ostream& write(const char * buffer, int len);

参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

#include<iostream>
using namespace std;
#include<fsteam>
class Person
{
    public:
    	char m_Name[64];
    	int m_Age;
}
void test01()
{
    //1.包含头文件
    //2.创建流对象
    ofstream ofs;
    //3.打开文件
    ofs.open("person.txt",ios::out|ios::binary);
    //4.写入
    Person p("张三",18);
    ofs.write((const char *)&p,sizeof(Person));
    //5.关闭文件
    ofs.close();
}
int main()
{
	test01();
	system("pause");
	return 0;
}
5.2.2读文件

二进制方式读文件主要利用流对象调用成员函数read
函数原型:

 istream& read(char *buffer, int len);

参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

#include<iostream>
using namespace std;
#include<fsteam>
class Person
{
    public:
    	char m_Name[64];
    	int m_Age;
}
void test01()
{
    //1.包含头文件
    //2.创建流对象
    ifstream ifs;
    //3.打开文件 判断文件是否打开成功
    ifs.open("person.txt",ios::in|ios::binary);
    if(!ifs.is_open())
    {
        cout<<"文件打开失败"<<endl;
        return;
    }
    //4.读文件
    Person p;
    ofs.read((char *)&p,sizeof(Person));
    cout<<"姓名:"<<p.m_Name<<endl;
    cout<<"年龄:"<<p.m_Age<<endl;
    //5.关闭文件
    ofs.close();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

流对象
ifstream ifs;
//3.打开文件并判断文件是否打开成功
ifs.open(“test.txt” ,ios::in);
if(!ifs.is_open())
{
cout<<“文件打开失败”<<endl;
return;
}
//4.读数据 四种方式读取
//第一种
char buf[1024]={0};
while(ifs>>buf)
{
cout<<buf<<endl;
}
//第二种
char buf[1024]={0};
while(ifs.getline(buf,sizeof(buf)))
{
cout<<buf<<endl;
}
//第三种
string buf;
while(getline(ifs,buf))
{
cout<<buf<<endl;
}
//第四种
char c;
while((c=ifs.get()!=EOF) //End Of File
{
cout<<c;
}
//5.关闭文件
ifs.close();
}
int main()
{
test01();
system(“pause”);
return 0;
}




### 5.2二进制文件

以二进制的方式对文件进行读写操作
打开方式要指定为**ios::binary**

#### 5.2.1写文件

二进制方式写文件主要利用流对象调用成员函数write
函数原型:

```c++
ostream& write(const char * buffer, int len);

参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

#include<iostream>
using namespace std;
#include<fsteam>
class Person
{
    public:
    	char m_Name[64];
    	int m_Age;
}
void test01()
{
    //1.包含头文件
    //2.创建流对象
    ofstream ofs;
    //3.打开文件
    ofs.open("person.txt",ios::out|ios::binary);
    //4.写入
    Person p("张三",18);
    ofs.write((const char *)&p,sizeof(Person));
    //5.关闭文件
    ofs.close();
}
int main()
{
	test01();
	system("pause");
	return 0;
}
5.2.2读文件

二进制方式读文件主要利用流对象调用成员函数read
函数原型:

 istream& read(char *buffer, int len);

参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

#include<iostream>
using namespace std;
#include<fsteam>
class Person
{
    public:
    	char m_Name[64];
    	int m_Age;
}
void test01()
{
    //1.包含头文件
    //2.创建流对象
    ifstream ifs;
    //3.打开文件 判断文件是否打开成功
    ifs.open("person.txt",ios::in|ios::binary);
    if(!ifs.is_open())
    {
        cout<<"文件打开失败"<<endl;
        return;
    }
    //4.读文件
    Person p;
    ofs.read((char *)&p,sizeof(Person));
    cout<<"姓名:"<<p.m_Name<<endl;
    cout<<"年龄:"<<p.m_Age<<endl;
    //5.关闭文件
    ofs.close();
}
int main()
{
	test01();
	system("pause");
	return 0;
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值