C++编程

2025-02-18 9 0

类和对象-静态成员

#include <iostream>
using namespace std;
class Person {
public:
	//静态成员函数
	static void func() {
		m_A = 100;//静态成员函数可以访问 静态成员变量
		//m_B = 200;//静态成员函数不可以访问 非静态成员变量,无法区分是哪一个对象的
		cout << "static func调用" << endl;
	}
	static int m_A;//静态
	int m_B;//非静态
	
	//静态成员函数也是有访问权限的
private:
	static void func2() {
		cout << "ststic void func2调用" << endl;
	}
};
int Person::m_A = 0;
void test01() {
	//通过对象访问
	Person p;
	//通过类访问
	p.func();
	Person::func();
	
	//Person::func2();类外访问不到私有静态成员函数
};
int main() {
	test01();
	return 0;
}

类和对象-对象特征-成员变量和成员函数分开存储

#include <iostream>
using namespace std;
class Person {
public:
	int m_a;//非静态成员变量,属于类的对象上
	static int m_b;//静态成员变量,不属于类的对象上
	void func(){}//非静态成员函数,不属于类的对象上
	static void func2(){}//静态成员函数,不属于类的对象上
};
 
void test01() {
	/*person p;
	cout << "size of p=" << sizeof(p) << endl;*/
	//c++编译器会给每个空对象分贝一个字节空间(为例区分空对象占的内存位置),每个空对象也应该有一个第一无二的内存地址
};
void test02() {
	Person p;
	cout << "size of p=" << sizeof(p) << endl;
}
 
int main() {
	test02();
	return 0;
}

类和对象-对象特征-this指针用途

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?

C++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象

his指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可

this指针的用途:
1.当形参和成员变量同名时,可用this指针来区分
2.在类的非静态成员函数中返回对象本身,可使用return *this

#include <iostream>
using namespace std;
//解决名字冲突
//返回对象本身用*this
class Person {
public:
	Person(int age) {
		this->age = age;//this指针指向 被调用函数 所属的对象
	}
	Person& Personage(Person& p) {
		this->age += p.age;
		//this指向p2的指针,而*this指向的就是p2这个对象本体
		return *this;
	}
	int age;
};
 
void test01() {
	Person p1(18);
	cout << "p1的年龄为:" << p1.age << endl;
}
void test02() {
	Person p1(10);
	Person p2(10);
	//p2.Personage(p1);
	p2.Personage(p1).Personage(p1).Personage(p1);//链式编程思想
	cout << "p2的年龄为:" << p2.age << endl;
}
 
int main() {
	//est01();
	test02();
	system("pause");
	return 0;
}

类和对象-对象特征-空指针访问成员函数

c++中空指针也是可以调用成员函数的,但也要注意有没有用到this指针

如果用到this指针,需要加以判断保证代码的健壮性

#include <iostream>
using namespace std;
//空指针调用成员函数
class Person {
public:
	void showclassname() {
		cout << "this is person class" << endl;
	}
	void showpersonage() {
		//不加if的报错原因是因为传入的指针为NULL
		if (this == NULL) {
			return;
		}
		cout << "age=" << m_Age << endl;
	}
	int m_Age;
};
 
void test01() {
	Person* p = NULL;
	p->showclassname();
	p->showpersonage();
}
void test02() {
	
}
 
int main() {
	test01();
	test02();
	system("pause");
	return 0;
}

类和对象-对象特征-const修饰成员函数

常函数:
成员函数后加const后我们称为这个函数为常函数
常函数内不可以修改成员属性
成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:
声明对象前加const称该对象为常对象
常对象只能调用常函数

#include <iostream>
using namespace std;
//常函数
class Person {
public:
	//this指针的本质 是指针常量 指针的指向是不可以被修改的
 
	void showPerson()const//等于const Person*const this 指针指向的值无法修改 
	{
		this->m_B = 100;
		//m_A = 100;
		//this=NULL;//this指针不可以修改指针的指向
	}
 
	void func()
	{
	}
 
	int m_A;
	mutable int m_B;//特殊的变量,即使在常函数中,也可以修改这个值
};
void test01() {
	Person p;
	p.showPerson();
	cout << "m_B:" << p.m_B << endl;
}
 
//常对象
void test02() {
	const Person p;//在对象前加const,变为常对象
	//p.m_A = 100;无法修改
	p.m_B = 100;//m_B是特殊值,在常对象下也可以修改
	//常对象只能调用常函数
	//p.func();无法调用//常对象 不可以地哦啊哦那个普通成员函数,因为普通成员函数可以修改属性
}
 
int main() {
	test01();
	test02();
	system("pause");
	return 0;
}

类和对象-对象特征-深拷贝与浅拷贝

深浅拷贝是面试经典问题,也是常见的一个坑
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作

#include <iostream>
using namespace std;
class person{
public:
person() {
cout << "默认构造函数" << endl;
}
person(int age,int high) {
m_age = age;
m_High = new int(high);
cout << "有参构造函数" << endl;
 
}
person(const person& p) {
cout << "拷贝构造函数" << endl;
m_age = p.m_age;
m_High = new int(*p.m_High);//若不申请新空间则会重复析构的delete
}
~person(){
if (m_High != NULL) {
delete m_High;
m_High = NULL;
}
cout << "析构函数调用" << endl;
}
int m_age;
int* m_High;
};
void test01() {
person p1(18,160);
cout << "p1的年龄为:" << p1.m_age << "身高为:" << *p1.m_High << endl;
person p2(p1);
cout << "p1的年龄为:" << p2.m_age << "身高为:" << *p2.m_High << endl;
}
int main() {
test01();
system("pause");
return 0;
}

类和对象-c++运算符重载

1.加号运算符

作用:实现两个自定义数据类型相加的运算。

#include <iostream>
using namespace std;
 
//加号运算符重载
class Person{
public:
	//1.成员函数重载+号
	/*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;
	}*/
	int m_A;
	int m_B;
};
 //2.全局函数重载+号 
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;
}
 
 
void test01() {
	Person p1;
	p1.m_A = 10;
	p1.m_B = 10;
	Person p2;
	p2.m_A = 10;
	p2.m_B = 10;
//成员函数重载的本质调用
//Person p3=p1.operator(p2);
 
//全局函数重载的本质调用
//Person p3=operator(p1,p2);
 
	Person 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;
}

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:
	//不能利用成员函数重载 左移运算符
	/*void operator<<() {
	}*/
	int m_A;
	int m_B;
};
 //利用全局函数重载左移运算符
ostream & operator << (ostream &cout,Person &p) //本质 operator<<(cout,p) 简化cout<<p
{
	cout << "m_A=" << p.m_A << "m_B=" << p.m_B ;
	return cout;
}
void test01() {
	Person p(10,10);
	cout << p <<endl;
}
 
 int main() {
	test01();
	system("pause");
	return 0;
}

3.递增运算符的重载

#include <iostream>
using namespace std;
 
//重载递增运算符
 
//自义定类型
class Myinteger {
	friend ostream& operator<<(ostream& cout, Myinteger myint);
public:
	Myinteger() {
		m_Num = 0;
	}
	
	//重载前置++运算符
	Myinteger& operator++()///不能去掉operator前的& 
	{
		m_Num++;
		//再将自身做返回
		return *this;
	}
	
	//重载后置++运算符
	Myinteger operator++(int)//int代表占位参数, 可以用于区分前置和后置
	{
		//先记录当前结果
		Myinteger temp = *this;
		//后递增
		m_Num++;
		//最后将记录结果做返回
		return temp;
	}
 
private:
	int m_Num;
};
 
//重载左移运算符
ostream& operator<<(ostream& cout, Myinteger myint) {
	cout << myint.m_Num;
	return cout;
}
 
void test01() {
	Myinteger myint;
	cout<<++(++myint)<<endl;
	cout << myint << endl;
}
void test02() {
	Myinteger myint;
	cout << myint++ << endl;
	cout << myint << endl;
}
 int main() {
	test01();
	test02();
	system("pause");
	return 0;
}

4.赋值运算符

C++编译器至少给一个类添加4个函数
1.默认构造函数(无参,函数体为空)

2.默认析构函数(无参,函数体为空)

3.默认拷贝构造函数,对属性进行值拷贝

4.赋值运算符operator=,对属性进行值拷贝

如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题

#include <iostream>
#include <string>
using namespace std;
 
//赋值运算符的重载
 
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(30);
	p3=p2 = p1;//赋值操作
	cout << "p1的年龄=" << *p1.m_Age << endl;
	cout << "p2的年龄=" << *p2.m_Age << endl;
	cout << "p3的年龄=" << *p3.m_Age << endl;
}
int main() {
	test01();
 
	system("pause");
	return 0;
}

5.关系运算符重载

作用:重载关系运算符,可以让两个自定义类型对象进行对比操作。

#include <iostream>
#include <string>
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 1;
		}
		else
			return 0;
	}
	string m_Name;
	int m_Age;
};
void test01() {
	Person p1("TOM", 18);
	Person p2("Jerry", 18);
	if (p1 == p2) {
		cout << "p1和p2是相等的" << endl;
	}
	else
	{
		cout << "p1和p2是不相等的" << endl;
	}
}
int main() {
	test01();
	system("pause");
	return 0;
}

6.函数调用运算符重载

1.函数调用运算符()也可以重载
2.由于重载后使用的方式非常像函数的调用,因此称为仿函数
3.仿函数没有固定写法,非常灵活

#include <iostream>
#include <string>
using namespace std;
 
//函数调用运算符重载
 
//打印输出类
 
class MyPrint {
public:
	//重载函数调用运算符
	void operator()(string test) {
		cout << test << endl;
	}
};
void MyPrint02(string test){
	cout << test << endl;
}
void test01() {
	MyPrint myprint;
 
	myprint("hello word");//由于使用起来非常类似于函数调用,因此称为仿函数
 
	MyPrint02("hello word");
}
//仿函数非常灵活,没有固定的写法
//加法类
 
class Myadd {
public:
	int operator()(int num1,int num2) {
		return num1 + num2;
	}
};
void test02() {
	Myadd add;
	int ret=add(100,100);
	cout << "ret=" << ret << endl;
	
	//匿名函数对象
	cout << Myadd()(100, 100) << endl;
}
int main() {
	test01();
	test02();
	system("pause");
	return 0;
}

类和对象-继承-基本语法

例如我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同
接下来我们分别利用普通写法和继承的写法来实现网页中的内容,看一下继承存在的意义以及好处

#include <stdio.h>
#include <iostream>
using namespace std;
 
//普通实现页面
 
//Java页面
class Java {
public:
	void header() {
		cout << "首页,公开课,登陆等" << endl;
	}
	void footer() {
		cout << "帮助中心,交流合作等" << endl;
	}
	void left() {
		cout << "Java,Python,C++等" << endl;
	}
	void content() {
		cout << "Java学习视频" << endl;
	}
};
//Python
class Python {
public:
	void header() {
		cout << "首页,公开课,登陆等" << endl;
	}
	void footer() {
		cout << "帮助中心,交流合作等" << endl;
	}
	void left() {
		cout << "Java,Python,C++等" << endl;
	}
	void content() {
		cout << "Python学习视频" << endl;
	}
};
//C++
class Cpp {
public:
	void header() {
		cout << "首页,公开课,登陆等" << endl;
	}
	void footer() {
		cout << "帮助中心,交流合作等" << endl;
	}
	void left() {
		cout << "java,python,c++等" << endl;
	}
	void content() {
		cout << "c++学习视频" << endl;
	}
};
 
void test01() {
	cout << "-------------------" << endl;
	cout << "Java下载视频页面如下: " << endl;
	Java a;
	a.header();
	a.footer();
	a.left();
	a.content();
 
	cout << "-------------------" << endl;
	cout << "Python下载视频页面如下: " << endl;
	Python p;
	p.header();
	p.footer();
	p.left();
	p.content();
 
	cout << "-------------------" << endl;
	cout << "C++下载视频页面如下: " << endl;
	Cpp c;
	c.header();
	c.footer();
	c.left();
	c.content();
}
int main() {
	test01();
	return 0;
}

使用继承后:

#include <stdio.h>
#include <iostream>
using namespace std;
 
//继承实现页面
//继承的好处:减少重复代码
// 语法:class子类 :继承方式 父类
// 子类 也称为 派生类
// 父类 也称为 基类
 
//公共实现页面
class BasePage {
	public:
		void header() {
			cout << "首页,公开课,登陆等" << endl;
		}
		void footer() {
			cout << "帮助中心,交流合作等" << endl;
		}
		void left() {
			cout << "Java,Python,C++等" << endl;
		}
		void content() {
			cout << "Java学习视频" << endl;
		}
	};
 
//Java页面
class Java :public BasePage {
public:
	void content() {
		cout << "Java学科视频" << endl;
	}
};
//Python
class Python :public BasePage {
public:
	void content() {
		cout << "Python学科视频" << endl;
	}
};
//Cpp
class Cpp :public BasePage {
public:
	void content() {
		cout << "C++学科视频" << endl;
	}
};
void test01() {
	cout << "-------------------" << endl;
	cout << "Java下载视频页面如下: " << endl;
	Java a;
	a.header();
	a.footer();
	a.left();
	a.content();
 
	cout << "-------------------" << endl;
	cout << "Python下载视频页面如下: " << endl;
	Python p;
	p.header();
	p.footer();
	p.left();
	p.content();
 
	cout << "-------------------" << endl;
	cout << "C++下载视频页面如下: " << endl;
	Python c;
	c.header();
	c.footer();
	c.left();
	c.content();
}
int main() {
	test01();
	return 0;
}

总结:

继承的好处:可以减少重复的代码
class A: public B;
A类称为子类 或派生类
B类称为父类 或基类
派生类中的成员,包含两大部分:
一类是从基类继承过来的,一类是自己增加的成员。
从基类继承过过来的表现其共性,而新增的成员体现了其个性。

类和对象-继承-继承方式

继承语法:class子类:继承方式 父类

继承方式一共有三种

1.公共继承

2.保护继承

3.私有继承

C++编程插图

#include <stdio.h>
#include <iostream>
using namespace std;
 
//继承方式
//公共继承
 
class Base1 {
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
 
class Son1 :public Base1 {
public:
	void func() {
		int m_A = 10;//父类的公共权限成员 到子类中依然是公共权限
		int m_B = 10;//父类的保护权限成员 到子类中依然是保护权限
		//int m_C=10;//父类的私有权限成员 子类访问不到
	}
};
void test01() {
	Son1 s1;
	s1.m_A = 100;
	//s1.m_B=100;//到Son1中m_B是保护权限 类外访问不到
}
 
//保护继承
class Base2 {
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
class Son2:protected Base2
{
public:
	void func() {
		m_A = 100;//父类中公共成员,到子类中变为保护权限
		m_B = 100;//父类中保护成员,到子类中变为保护权限
		//m_C = 100;//父类中私有成员 子类访问不到
	}
};
void test02() {
	Son2 s1;
	//s1.m_A=1000;//在Son2中m_A变为保护权限,因此类外访问不到
	//s1.m_B=1000;//在Son2中m_B保护权限,不可以访问
}
//私有继承
class Base3 {
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
class Son3 :private Base3 {
public:
	void  func() {
		m_A = 100;//父类中公共成员 到子类中变为 私有成员
		m_B = 100;//父类中保护成员 到子类中变为 私有成员
		//m_C = 100;//父类中私有成员,子类访问不到
	}
};
class GrandSon3 :public Son3 {
public:
	void  func() {
		//m_A = 1000;//到了Son3中 m_A变为私有,即使是孙子,也是访问不到的
		//m_B = 1000;//到了Son3中 m_A变为私有,即使是孙子,也是访问不到的
	}
};
void test03() {
	Son3 s1;
	//s1.m_A = 1000;//到Son3中 变为 私有成员 类外访问不到
	//s1.m_B = 1000;//到Son3中 变为 私有成员 类外访问不到
}
int main() {
	test01();
	return 0;
}

类和对象-继承-继承中的对象模型

问题:从父类继承过来的成员,那些属于子类对象中

#include <stdio.h>
#include <iostream>
using namespace std;
 
//继承中的对象模型
 
class Base {
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
 
class Son :public Base {
public:
	int m_D;
};
 
void test01() {
	//父类中所以非静态成员数学都会被子类继承下去
	//父类中私有成员属性 是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了
	cout << "size of Son=" << sizeof(Son) << endl;
}
 
int main() {
	test01();
	return 0;
}

类和对象-继承-构造析构顺序

子类继承父类后,当创建子类对象,也会调用父类的析构函数

问题:父类和子类的构造和析构顺序是谁先谁后?

#include <stdio.h>
#include <iostream>
using namespace std;
 
//继承中的构造和析构顺序
class Base {
public:
	Base() {
		cout << "Base的构造函数" << endl;
	}
	~Base() {
		cout << "Base析构函数"<<endl;
	}
};
class Son :public Base {
public:
	Son() {
		cout << "Son的构造函数" << endl;
	}
	~Son() {
		cout << "Son析构函数" << endl;
	}
};
 
void test01() {
	//Base b;
 
	//继承中的构造和析构顺序如下:
	//先构造父类,再构造子类,析构的顺序与构造的顺序相反
	Son s;
}
 
int main() {
	test01();
	return 0;
}

总结:继承中先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反(先调用子类析构函数再调用父类析构函数)

类和对象-继承-同名成员处理

问题:当子类与仪类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

访问子类同名成员 ,直接访问即可
访问父类同名成员,需要加作用域

#include <stdio.h>
#include <iostream>
using namespace std;
 
//继承中同名成员处理
class Base {
public:
	Base() {
		m_A = 100;
	}
	int m_A;
	void func() {
		cout << "Base下的func调用" << endl;
	}
	void func(int a) {
		cout << "Base下的func(int a)调用" << endl;
	}
};
class Son :public Base {
public:
	Son() {
		m_A = 200;
	}
	int m_A;
	void func() {
		cout << "Son下的func调用" << endl;
	}
};
//同名的成员属性处理
void test01() {
	Son s;
	cout << "Son 下的m_A=" << s.m_A << endl;
	//如果通过子类对象 访问到父类中同名成员,需要加作用域
	cout << "Base 下的m_A=" << s.Base::m_A << endl;
}
//同名的成员函数处理
void test02() {
	Son s;
	s.func();
	s.Base::func();
	//如果子类中出现和父类同名的成员函数,子类同名成员会隐藏掉父类中所有同名成员函数
	//如果想访问到父类中被隐藏的同名成员函数,需要加作用域
	s.Base::func(100);
}
 
int main() {
	test01();
	test02();
	return 0;
}

总结:
1.子类对象可以直接访问到子类中同名成员
2.子类对象加作用域可以访问到父类同名成员
3.当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

类和对象-继承-同名静态成员处理

问题:继承中同名的静态成员在子类对象上如何进行访问?

静态成员和非静态成员出现同名,处理方式一致

访问子类同名成员 直接访问即可
访问父类同名成员 需要加作用域

#include <stdio.h>
#include <iostream>
using namespace std;
 
//继承中的同名静态成员处理方式
 
class Base {
public:
	static int m_A;
 
	static void func() {
		cout << "Base下static void func的函数调用" << endl;
	}
};
int Base::m_A = 100;
class Son:public Base {
public:
	static int m_A;
 
	static void func() {
		cout << "Son下static void func的函数调用" << endl;
	}
};
int Son::m_A = 200;
 
//同名静态成员属性
void test01() {
 
	//1.通过对象来访问
	Son s;
	cout << "m_A=" << s.m_A << endl;
	cout << "m_A=" << s.Base::m_A << endl;
 
	//2.通过类名来访问
	cout << "通过类名来访问" << endl;
	cout << "Son下m_A=" << Son::m_A << endl;
	//第一个::代表通过类名的方式来访问  第二个::代表访问父类作用域下
	cout << "Base下m_A=" << Son::Base::m_A << endl;
}
//同名静态成员函数
void test02() {
	//通过对象访问
	cout << "通过对象访问" << endl;
	Son s;
	s.func();
	s.Base::func();
	
	//通过类名访问
	cout << "通过类名来访问" << endl;
	Son::func();
	Son::Base::func();
}
int main() {
	test01();
	test02();
	return 0;
}

总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通过类名)

类和对象-继承-继承语法

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

语法:class子类:继承方式  父类1 ,继承方式  父类2...

多继承可能会引发父类中有同名成员出现,需要加作用域区分

C++实际开发中不建议用多继承

#include <stdio.h>
#include <iostream>
using namespace std;
 
//多继承语法
 
class Base1 {
public:
	Base1() {
		m_A = 100;
	}
	int m_A;
};
 
class Base2 {
public:
	Base2() {
		m_A = 200;
	}
	int m_A;
};
//子类 需要继承Base1和Base2
//语法:class子类:继承方式 父类1,继承方式 父类2..
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 << "sizeof Son=" << sizeof(s) << endl;
	//当父类中出现同名成员,需要加作用于区分
	cout << "Base1 m_A=" << s.Base1::m_A << endl;
	cout << "Base2 m_A=" << s.Base2::m_A << endl;
}
int main() {
	test01();
	return 0;
}

总结:多继承中如果父类中出现了同名情况,子类使用时要加作用域。

类和对象-继承-菱形继承问题以及解决方案

菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承者两个派生类
这种继承被称为菱形继承,或者钻石继承

典型的菱形继承案例:

C++编程插图1

菱形继承问题:
1.羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
2.草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。

#include <stdio.h>
#include <iostream>
using namespace std;
 
//动物类
class Animal {
public:
	int m_A;
};
//利用虚继承 可以解决菱形继承问题 
//在继承之前加上 关键字 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_A = 18;
	st.Tuo::m_A = 28;
	//当菱形继承,两个父类具有相同的数据,需要加以作用域区分。
	cout << "st.Sheep::m_A =" << st.Sheep::m_A << endl;
	cout << "st.Tuo::m_A = " << st.Tuo::m_A << endl;
	cout << "st,m_A=" << st.m_A << endl;
	//这份数据我们知道 只要有一份即可,菱形继承导致数据有两份,资源浪费。
 
}
int main() {
	test01();
	return 0;
}

总结:
菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
利用虚继承可以解决菱形继承问题

类和对象-多态-多态的基本语法

多态是C++面向对象三大特性之一
多态分为两类
1.静态多态:函数重载和近算符重载属于静态多态,复用函数名
2.动态多态:派生类虚函数实现运行时多态

静态多态和动态多态区别:
1.静态多态的函数地址早绑定-编译阶段确定函数地址
2.动态多态的函数地址晚绑定-运行阶段确定函数地址

下面通过案例进行讲解多态:

#include <stdio.h>
#include <iostream>
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() {
		cout << "小狗在说话" << endl;
	}
};
//执行说话的函数
//地址早绑定 在编译阶段确定函数地址
//如果想执行让猫说话,那么这个函数就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定
 
//动态多态满足条件
//1.有继承关系
//2.子类要重写父类虚函数
 
//动态多态使用
//父类的指针或者引用 执行子类对象
 
void dospeak(Animal &animal)//Animal & animal=cat;
{
	animal.speak();
}
void test01() {
	Cat cat;
	dospeak(cat);
 
	Dog dog;
	dospeak(dog);
 
	Animal animal;
	dospeak(animal);
}
int main() {
	test01();
	return 0;
}

类和对象-多态-案例1-计算器类

案例描述:
分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类

多态的优点:
1.代码组织结构清晰
2.可读性强
3.利于前期和后期的扩展以及维护

#include <stdio.h>
#include <iostream>
#include<string>
using namespace std;
 
//分别利用普通写法和多态技术实现计算器类
 
//普通写法
class Calculator {
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;
		}
		//如果要扩展新的功能,需要修改源码
		//在真实的开发中 提倡 开闭原则
		//开闭原则:对扩展进行开放,对修改进行关闭
	}
	int m_Num1;
	int m_Num2;
};
 
void test01() {
	//创建计算器对象
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 20;
 
	cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl;
	cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl;
	cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl;
}
 
 
 
//利用多态实现计算器
 
//多态好处:
// 1.组织结构清晰
// 2.可读性强
// 3.对于前期和后期扩展以及维护性高
 
//实现计算器的抽象类
class AbstractCalculator {
public:
	virtual int getResult() {
		return 0;
	}
	int m_Num1;
	int m_Num2;
};
//加法计算器类
class AddCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 + m_Num1;
	}
};
//减法计算器类
class SubCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 - m_Num1;
	}
};
//乘法计算器类
class MulCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 * m_Num1;
	}
};
void test02() {
	//多态使用条件
	//父类指针或者引用指向子类对象
 
	//加法运算
	AbstractCalculator* abc = new AddCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;
 
	cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
	//用完后记得销毁
	delete abc;
 
	//减法运算
	abc = new SubCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;
 
	cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;
 
	//乘法运算
	abc = new MulCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;
 
	cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;
}
int main(){
	//test01();
	test02();
	system("Pause");
	return 0;
}

总结:C++开发提倡利用多态设计程序架构,因为多态优点很多

类和对象-多态- 纯虚函数和抽象类

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

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

纯虚函数语法:
virtual  返回值类型  函数名 (参数列表)=0;

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

抽象类特点:
1.无法实例化对象
2.子类必须重写抽象类中的纯虚函数,否则也属于抽象类

#include <stdio.h>
#include <iostream>
#include<string>
using namespace std;
 
//纯虚函数和抽象类
class Base
{
public:
	//纯虚函数
	//只要有一个纯虚函数,这个类称为抽象类
	//抽象类的特点:
	//1.无法实例化对象
	//2.抽象类的子类 必须要 =重写父类中的纯虚函数,否则也属于抽象类
	virtual void func() = 0;
};
class Son :public Base {
public:
	virtual void func() {
		cout << "func函数调用" << endl;
	}
};
void test01() {
	//Base b;//抽象类无法实例化对象
	//new Base;//抽象类无法实例化对象
 
	//Son s;//子类中必须重写父类中的纯虚函数,否则无法实例化对象
	Base* base = new Son;
	base->func();
}
int main() {
	test01();
	system("pause");
	return 0;
}

类和对象-多态-案例2-制作饮品

案例描述:
制作饮品的大致流程为:煮水-冲泡-倒入杯中-加入辅料

利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

#include <stdio.h>
#include <iostream>
#include<string>
using namespace std;
 
//多态的案例2 制作饮品
class Abstractdrinking
{
public:
	//煮水
	virtual void Boil() = 0;
	//冲泡
	virtual void Brew() = 0;
	//导入杯中
	virtual void Pourincup() = 0;
	//加入辅料
	virtual void Putsomething() = 0;
 
	//制作饮品
	void makedrink() {
		Boil();
		Brew();
		Pourincup();
		Putsomething();
	}
};
class coffee :public Abstractdrinking {
	//煮水
	virtual void Boil() {
		cout << "煮农夫山泉" << endl;
	}
	//冲泡
	virtual void Brew() {
		cout << "冲泡咖啡" << endl;
	}
	//导入杯中
	virtual void Pourincup() {
		cout << "导入杯中" << endl;
	}
	//加入辅料
	virtual void Putsomething(){
		cout << "加入糖和牛奶" << endl;
	}
 
};
class tea:public Abstractdrinking {
	//煮水
	virtual void Boil() {
		cout << "煮矿泉水" << endl;
	}
	//冲泡
	virtual void Brew() {
		cout << "冲泡茶叶" << endl;
	}
	//导入杯中
	virtual void Pourincup() {
		cout << "导入杯中" << endl;
	}
	//加入辅料
	virtual void Putsomething() {
		cout << "加入柠檬" << endl;
	}
 
};
void dowork(Abstractdrinking *abs) //Abstractdrinking *abs=new coffee
{
	abs->makedrink();
	delete abs;//释放
}
void test01() {
	dowork(new coffee);
	cout << "------------" << endl;
	dowork(new tea);
}
int main() {
	test01();
	system("pause");
	return 0;
}

类和对象-多态-虚析构和纯虚析构

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

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

虚析构和纯虚析构共性:
·可以解决父类指针释放子类对象
·都需要有具体的函数实现
虚析构和纯虚析构区别:
·如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:
virtual ~类名(){]
纯虚析构语法:
virtual ~类名()= 0;
类名: :~类名(){]

#include <stdio.h>
#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() = 0;
};
Animal:: ~Animal() {
	cout << "Animal的纯虚析构析构函数调用" << endl;
}
class Cat :public Animal {
public:
	Cat(string name) {
		cout << "Cat构造函数调用" << endl;
		m_Name=new string(name);
	}
	virtual void speak() {
		cout << *m_Name<<"小猫在说话" << endl;
	}
	~Cat()
	{
		if (m_Name != NULL) {
			cout <<"Cat析构函数调用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}
	string* m_Name;
};
void test01() {
	Animal* animal = new Cat("Tom");
	animal->speak();
	//父类指针在析构时候, 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
	delete animal;
}
int main() {
	test01();
	system("pause");
	return 0;
}

总结:
1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
2.如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
3.拥有纯虚析构函数的类也属于抽象类

类和对象-多态-案例3-电脑组转需求分析

电脑主要组成部件为CPU(用于计算),显卡(用于显示),内存条(用于存储)
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作

#include <stdio.h>
#include <iostream>
#include<string>
using namespace std;
 
//抽象不同零件类
//抽象CPU类
class CPU{
public:
	//抽象的计算函数
	virtual void calculate() = 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->calculate();
		m_vc->display();
		m_mem->storage();
	}
	//提供一个析构函数 释放三个电脑零件
	~Computer()
	{
		//释放CPU零件
		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;//CPU的零件指针
	VideoCard* m_vc;//显卡的零件指针
	Memory* m_mem;//内存条的零件指针
};
 
//具体厂商
//Intal厂商
class IntelCPU:public CPU {
public:
	virtual void calculate() {
		cout << "Intel的CPU开始计算了" << endl;
	}
};
class IntelVideoCard :public VideoCard {
public:
	virtual void display() {
		cout << "Intel的显卡开始显示了" << endl;
	}
};
class IntelMemory :public Memory {
public:
	virtual void storage() {
		cout << "Intel的内存条开始显示了" << endl;
	}
};
 
//Lenovo厂商
class LenovoCPU :public CPU {
public:
	virtual void calculate() {
		cout << "Lenovo的CPU开始计算了" << endl;
	}
};
class LenovoVideoCard :public VideoCard {
public:
	virtual void display() {
		cout << "Lenovo的显卡开始显示了" << endl;
	}
};
class LenovoMemory :public Memory {
public:
	virtual void storage() {
		cout << "Lenovo的内存条开始显示了" << endl;
	}
};
 
void test01() {
	cout << "第一台电脑开始工作:" << endl;
	//第一台电脑零件
	CPU* intelCpu = new IntelCPU;
	VideoCard* intelCard = new IntelVideoCard;
	Memory* intelMen = new IntelMemory;
 
	//创建第一台电脑
	Computer* computer1 = new Computer(intelCpu, intelCard, intelMen);
	computer1->work();
	delete computer1;
 
	cout << "-----------------" << endl;
	cout << "第二台电脑开始工作:" << endl;
	//第二台电脑组装
	Computer* computer2= new Computer(new LenovoCPU,new LenovoVideoCard ,new LenovoMemory );
	computer2->work();
	delete computer2;
 
	cout << "-----------------" << endl;
	cout << "第三台电脑开始工作:" << endl;
	//第二台电脑组装
	Computer* computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);
	computer3->work();
	delete computer3;
};
int main() {
	test01();
	system("pause");
	return 0;
}

C++文件操作-文本文件-写文件

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
C++中对文件操作需要包含头文件<fstream

文件类型分为两种:
1.文本文件-文件以文本的ASCII码形式存储在计算机中
2.二进制文件-文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们

操作文件的三大类:
1.ofstream:写操作
2. ifstream:读操作
3.fstream:读写操作

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

C++编程插图2

#include <iostream>
#include<fstream>//头文件包含
using namespace std;
 
//文本文件 写文件
void test01() {
	//1.包含头文件fstream
	//2.创建流对象
	ofstream ofs;
 
	//3.指定打开方式
	ofs.open("test.text", ios::out);
 
	//4.写内容
	ofs << "姓名:张三" << endl;
	ofs << "姓别:男" << endl;
	ofs << "年龄:18" << endl;
	//5.关闭文件
	ofs.close();
}
int main() {
	test01();
	system("pause");
	return 0;
}

总结:
1.文件操作必须包含头文件fstream
2.读文件可以利用ofstream,或者fstream类
3.打开文件时候需要指定操作文件的路径,以及打开方式
4.利用<<可以向文件中写数据
5.操作完毕,要关闭文件

C++文件操作-文本文件-读文件

读文件与写文件步骤相似,但是读取方式相对于比较多

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

#include <iostream>
using namespace std;
#include<fstream>
#include<string>
 
//文本文件 读文件
void test01() {
	//1.包含头文件
 
	//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)//EOF end of file
	{
		cout << c;
	}
 
	//5.关闭文件
	ifs.close();
}
 
int main() {
	test01();
	return 0;
}

总结:
1.读文件可以利用ifstream,或者fstream类

2.利用is_open函数可以判断文件是否打开成功

3.close 关闭文件

C++文件操作-二进制文件-写文件

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

写文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型:
ostream& write(constchar * buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

#include <iostream>
using namespace std;
#include<fstream>
 
//二进制文件 写文件
class Person {
public:
	char m_Name[64];//姓名
	int m_Age;//年龄 
};
 
void test01() {
	//1.包含头文件
 
	//2.创建流文件
	ofstream ofs("Person.text", ios::out | ios::binary);
	//3.打开文件
	//ofs.open("Person.text", ios:: out | ios::binary);
	//4.写文件
	Person P = { "张三",18 };
	ofs.write((const char*)&P, sizeof(Person));
	//5.关闭文件
	ofs.close();
}
int main() {
	test01();
	return 0;
}

读文件
二进制方式读文件主要利用流对象调用成员函数read
函数原型:
istream& read(char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

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

4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

Web应用&企业产权&域名资产&网络空间&威胁情报
【CTF】Python Jail沙箱逃逸手法总结 PyJail All in One
风投巨头Insight Partners遭遇网络攻击,敏感数据或泄露
网络犯罪转向社交媒体,攻击量达历史新高
雅虎数据泄露事件:黑客涉嫌兜售60.2万个电子邮件账户
黑客如何利用提示词工程操纵AI代理?

发布评论