C++学习笔记(超详细笔记记录ing)

C++学习笔记(12)

学习是一件任重而道远的事情,与其焦虑不如动手起来,借助平台记录自己学习笔记,希望和大家多多交流,今天又是努力成为程序媛的一天!

17.类和对象

17.6 继承

继承是面向对象三大特性之一
对于类,下级别的成员除了拥有上一级的共性,还有自己的特点

17.6.1 继承的基本语法

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

子类又称为派生类
父类又称为基类

#include<iostream>
using namespace std;


//避免重复代码  用继承实现

//写一个公共的页面代码
class Basepage {
public:
	void header() {
		cout << "首页、公开课、登录、注册..." << endl;
	}

	void footer() {
		cout << "帮助中心、交流合作" << endl;
	}

	void left() {
		cout << "Java、Python、C++" << endl;
	}
};

//Java页面
class Java :public Basepage{//继承共同的页面
public:
	void content() {
		cout << "Java 学科视频" << endl;
	}
};

//Python页面
class Python :public Basepage {//继承共同的页面
public:
	void content() {
		cout << "Python 学科视频" << endl;//写自己特点内容
	}
};

//C++页面
class CPP :public Basepage {//继承共同的页面
public:
	void content() {
		cout << "C++ 学科视频" << endl;
	}
};

void test() {
	Java j;
	Python p;
	CPP c;
	j.header();
	j.left();
	j.footer();
	j.content();

	cout << "-----------------------------------" << endl;
	p.header();
	p.left();
	p.footer();
	p.content();

	cout << "-----------------------------------" << endl;
	c.header();
	c.left();
	c.footer();
	c.content();

}

int main() {
	test();

	system("pause");
	return 0;
}

在这里插入图片描述

17.6.2 继承方式

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

  • 继承方式三种:

    • 1.公共继承
    • 2.保护继承
    • 3.私有继承
      在这里插入图片描述
#include<iostream>
using namespace std;

//继承方式
class Base {
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;

};

//派生类
//公有继承
//在公有继承中 m_a是Son1的公共成员 m_b是Son1的保护成员 m_c在Son1不可访问 即父类的私有属性不管怎么都不可访问
class Son1:public Base {
public:
	void fun1() {
		m_a = 1000;//公共 类内类外可访问
		m_b = 20;//保护 类内可访问
		//m_c = 20;//报错 不可访问
	}

};

void test01() {

	Son1 s1;
	s1.fun1();
	s1.m_a = 22;//公共成员 类外可访问
	//s1.m_b = 22;//保护成员 ,类外不可访问
	//s1.m_c = 22;//不可访问

}

//保护继承
//在保护继承中 m_a是Son1的保护成员 m_b是Son1的保护成员 m_c在Son1不可访问 即父类的私有属性不管怎么都不可访问
class Son2 :protected Base {
public:
	void fun2() {
		m_a = 22;//保护 类内可访问,类外不可
		m_b = 23;//保护 类内可访问
		//m_c = 20;//报错 不可访问
	}
};

void test02() {
	Son2 s2;
	s2.fun2();
	//s2.m_a = 22;//保护成员 类外不可访问
	//s1.m_b = 22;//保护成员 ,类外不可访问
	//s1.m_c = 22;//不可访问
}


//私有继承
//在私有继承中 m_a是Son1的私有成员 m_b是Son1的私有成员 m_c在Son1不可访问 即父类的私有属性不管怎么都不可访问
class Son3 :private Base {
public:
	void fun3() {
		m_a = 22;//私有 类内可访问,类外不可
		m_b = 23;//私有 类内可访问
		//m_c = 20;//报错 不可访问
	}
};

void test03() {
	Son3 s3;
	s3.fun3();
	//s2.m_a = 22;//私有成员 类外不可访问
	//s1.m_b = 22;//私有成员 ,类外不可访问
	//s1.m_c = 22;//不可访问
}


//继承子类的
//公有继承子类的Son3 即继承父类的所有私有属性 那么都无法访问 继承原理一致
class Sun :public Son3 {
public:
	void fun4() {
		//m_a = 22;//父亲的私有属性无法访问
		//m_b = 23;//父亲的私有属性无法访问
		//m_c = 20;//报错 不可访问
	}
};


//保护继承 子类的保护继承
class Sun2 :protected Son2 {
public:
	void fun5() {
		m_a = 222;
		m_b = 333;
		//m_c = 444;//报错  不可访问
	}
};

void test05() {
	Sun2 ss2;
	//ss2.m_a = 10;//保护成员类外无法访问
	//ss2.m_a = 10;//保护成员类外无法访问
	//ss2.m_a = 10;//报错
}

int main() {
	test01();
	test02();
	test03();
	test05();

	system("pause");
	return 0;
}

注意:三种成员特点如下:公有权限,私有权限,保护权限

17.6.3 继承中的对象模型

#include<iostream>
using namespace std;

class Base {
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};


class Son :public Base {
public:
	void fun1() {
		m_a = 1000;
		m_b = 20;
		//m_c = 20;
	}

	int m_d;

};

class Son3 :private Base {
public:
	void fun3() {
		m_a = 22;//私有 类内可访问,类外不可
		m_b = 23;//私有 类内可访问
		//m_c = 20;//报错 不可访问
	}
	int m_d3;
};


void test() {
	cout << "size of son = " << sizeof(Son) << endl;//16
	cout << "size of son = " << sizeof(Son3) << endl;//16
}

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

int main() {
	test();

	system("pause");
	return 0;
}

注意:
1.父类中所有的非静态成员属性都会被子类继承下去
2.父类中私有成员属性 是被编译器隐藏了 因此访问不到 但确实被继承下去了

(Windows)用工具查看继承中的对象属性:
1.打开编写的VS版本中的开发人员命令提示符 我这里显示是英文Developer Command Prompt for VS 2022 Preview
2.转到文件路径下:右击目前项目,打开所在文件夹,查看路径,如果是在当前盘符就不用跳转,如果命令符打开盘符不在当前路径盘符,就在命令行后面输 入文件所在盘符加上冒号跳转,再cd 到当前项目文件夹路径
3.输入dir查看目录内容
4.cl / d1 reportSingleClassLayout类名 文件名 报告单个类布局 第一个是字母l,第二个是数字1
5.可以查看该类的所有属性,哪些是父类和自己的都能显示出来
在这里插入图片描述

17.6.4 继承中的构造和析构顺序

继承中父类和子类的构造和析构顺序:创建子类对象时,会先创建一个父类出来,即先是父类的构造 然后子类构造,之后析构子类先 父类后

#include<iostream>
using namespace std;

class Base {
public:
	Base() {
		cout << "这是父类的构造函数调用" << endl;
	}

	~Base() {
		cout << "这是父类的析构函数调用" << endl;
	}
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};


class Son :public Base {
public:
	Son() {
		cout << "这是子类的构造函数调用" << endl;
	}

	~Son() {
		cout << "这是子类的析构函数调用" << endl;
	}

	int m_d;

};

void test() {
	//继承中构造和析构顺序如下:
	//先构造父类 再构造子类 析构顺序和构造相反
	Son s;
}

int main() {
	test();

	system("pause");
	return 0;
}

在这里插入图片描述
继承中构造和析构顺序如下:
先构造父类 再构造子类 析构顺序和构造相反

17.6.5 继承同名成员处理方式

前情提要:如果子类和父类出现同名成员,如何通过子类对象访问子类或父类中同名数据?
1.访问子类同名成员 直接访问即可
2.访问父类同名成员 需要加作用域

#include<iostream>
using namespace std;

class Base {
public:
	Base() {
		m_a = 18;
	}
	void fun() {
		cout << "父类成员函数" << endl;
	}
	void fun(int a) {
		cout << "父类成员函数2" << endl;
	}
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};


class Son :public Base {
public:
	Son() {
		m_a = 100;
	}
	void fun() {
		cout << "子类成员函数" << endl;
	}
	int m_a;
	int m_d;
};

void test() {
	//直接访问是子类自己的成员
	//如果想要通过子类对象访问父类的同名成员需要加一个作用域
	Son s;
	cout << "m_a = " << s.m_a << endl;//100
	cout << "Base m_a  =  " << s.Base::m_a << endl;//18
	s.fun();//只有父类有该成员函数时,公共权限也是子类成员 可以直接调用 但是如果与子类重名就调用子类的
	s.Base::fun();//重名时只有加父类作用域才会调用父类
	//s.fun(100);//会报错 虽然是父类含参的同名函数但是当子类出现与父类同名的成员函数 
	//子类的同名成员会隐藏父类中所有的同名成员函数 即所有重载的函数
	//如果想访问就加作用域
	s.Base::fun(100);//父类成员函数2


}

int main() {
	test();


	system("pause");
	return 0;
}

注意:

  • 子类和父类有同名成员时,子类对象直接访问的是子类中的同名成员
  • 子类对象加作用域可以访问到父类同名成员
  • 当子类和父类拥有同名的成员函数,子类会隐藏父类中所有的同名函数,包括本身和重载函数,加作用域可以访问到父类中同名函数

17.6.6 继承同名静态成员处理方式

前情提要:如果子类和父类出现同名成员,静态成员如何通过子类对象访问子类或父类中同名数据?
处理方式和非静态成员处理方式一致:
1.访问子类同名成员 直接访问即可
2.访问父类同名成员 需要加作用域

#include<iostream>
using namespace std;

class Base {
public:
	
	static void fun() {
		cout << "父类成员函数" << endl;
	}
	static void fun(int a) {
		cout << "父类成员函数2" << endl;
	}
	static int m_a;//静态成员所有对象共享一份数据 类内声明 类外初始化
protected:
	int m_b;
private:
	int m_c;
};

int Base::m_a = 100;


class Son :public Base {
public:
	
	static void fun() {
		cout << "子类成员函数" << endl;
	}
	static int m_a;
	int m_d;
};
int Son::m_a = 120;

void test01() {
	//1.通过对象来访问
	Son s1;
	cout << "----------通过对象访问 -----------" << endl;
	cout << "Son 下 m_a = " << s1.m_a << endl;
	//2.通过类名来访问
	cout << "------------通过类名访问--------------- " << endl;
	cout << "Son 下 m_a = " << Son::m_a << endl;
}

void test02() {
	//1.通过对象来访问
	Son s2;
	cout << "---------通过对象访问--------- " << endl;
	s2.fun();
	//2.通过类名来访问
	cout << "------------通过类名访问---------- " << endl;
	Son::fun();
}

void test03() {
	//访问父类同名静态成员和函数
	//1.通过对象的方式
	Son s3;
	cout << "---------通过对象访问-------- " << endl;
	cout << "Son 下 m_a = " << s3.Base::m_a << endl;
	s3.Base::fun();

	//2.通过子类的类名的方式
	cout << "---------通过子类类名访问--------- " << endl;
	cout << "Base 下 m_a = " << Son::Base::m_a << endl;
	//第一个::代表通过类名的方式访问 第二个::表示访问父类作用域下
	Son::Base::fun();
	
	//Son::fun(100)//报错  //子类出现和父类同名静态成员函数 也会隐藏父类中所有的同名成员函数
	//如果想访问父类中被隐藏的同名成员 需要加作用域
	Son::Base::fun(100);
}

int main() {

	test01();
	test02();
	test03();

	system("pause");
	return 0;
}

在这里插入图片描述

总结:同名静态成员处理和同名成员处理原理方式基本一致,不过静态成员还可以通过类名的方式访问,这点普通成员没有,以及静态成员属性要类内声明,类外初始化。

17.6.7 多继承语法

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

多继承可能引起父类同名成员出现,所以通常会加作用域区分

实际开发并不推荐多继承使用

#include<iostream>
using namespace std;

class Base1 {
public:
	Base1() {
		m_a = 18;
	}
	void fun() {
		cout << "父类成员函数" << endl;
	}
	void fun(int a) {
		cout << "父类成员函数2" << endl;
	}
	int m_a;

};


class Base2 {
public:
	Base2() {
		m_b = 28;
		m_a = 30;
	}
	void fun() {
		cout << "父类成员函数" << endl;
	}
	void fun(int a) {
		cout << "父类成员函数2" << endl;
	}
	int m_b;
	int m_a;

};

//多继承语法 逗号隔开
class Son :public Base1,public Base2 {
public:
	Son() {
		m_c = 100;
		m_d = 500;
	}
	void fun() {
		cout << "子类成员函数" << endl;
	}
	int m_c;
	int m_d;
};


void test01() {
	Son s1;
	//每个Son类创建的对象所占的字节大小
	cout << "size of Son = " << sizeof(Son) << endl;//这里Son换成s1也可  size of Son = 20
	//没有同名时可以直接调用s1.m_a不会错
	//但父类中出现同名成员 需要加作用域区分
	cout << "Base1::m_a = " << s1.Base1::m_a << endl;//18
	cout << "Base2::m_a = " << s1.Base2::m_a << endl;//30


}


int main() {
	test01();
	

	system("pause");
	return 0;
}

父类没有同名成员时可以直接对象调用,但是出现同名成员时要加作用域,实际开发时,代码通常多人合作,不清楚是否存在同名情况,所以多继承编写代码较繁琐,不建议使用

17.6.8 菱形继承

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

#include<iostream>
using namespace std;

class Base {
public:
	int m_age;
};
//利用虚继承 解决菱形继承问题
//在继承之前加上关键字virtual变为虚继承
//Base类称为 虚基类


class Son1 :virtual public Base {};//虚继承

class Son2 :virtual public Base {};//虚继承

class Sun :public Son1,public Son2 {};

void test() {
	Sun s1;
	//s1.m_age = 18;//会报错  Son1和Son2都有这个属性 出现二义性
	s1.Son1::m_age = 25;
	s1.Son2::m_age = 28;
	//当出现菱形继承 两个父类有相同属性  用作用域区分
	cout << "s1.Son1::m_age = " << s1.Son1::m_age << endl;
	cout << "s1.Son2::m_age = " << s1.Son2::m_age << endl;
	//然而这个数据只需要一份 菱形继承导致数据有两份 资源浪费
	//虚继承后数据只有一个  以最后为准  这时按照对象直接调用也不会出错
	cout << "s1.m_Age = " << s1.m_age << endl;
	//底层逻辑:这时Sun继承的不是两个数据 而是两个指针,这两个指针通过偏移量找到唯一的数据
}


int main() {
	test();


	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述
底层逻辑:这时Sun继承的不是两个数据 而是两个指针,这两个指针通过偏移量找到唯一的数据

第十二篇笔记到此结束,C++基础学习会持续更新在C++学习笔记合集中,当作学习笔记复习,如果能帮助其他小伙伴就更好了。
笔记是看黑马程序C++时做的记录,笔记中如果有错误和改进的地方,欢迎大家评论交流,up up up!!!
学习原视频来自:黑马程序员C++从0到1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值