C++复习笔记12

       继承是指子类继承父类的属性和行为并同时可以拥有自己的属性和行为这一特性。继承是类设计层次的复用。体现了面向对象的特性。

继承语法:class 子类:继承方式 父类 {...};

      继承方式和访问限定符:

       不同继承方式下继承不同权限的基类成员,在子类中的访问权限如下:

 重点:1.子类内部不能访问父类的私有成员。

            2.子类中的成员权限结果是继承方式和父类权限取最小值。权限大小值       public>protected>private.但是父类中的private成员无论被如何继承都无法访问。

            3.不写继承方式时,默认是私有继承,与类成员的默认访问属性一个道理。

            4.class的默认继承方式为私有继承,struct的默认继承方式为私有,与成员默认访问属性一个道理。

           6.通常使用public继承方式,且显示给出。

子类内部不能访问父类的私有成员:

#include<iostream>
using namespace std;

class Person
{
public:
	void Print()
	{
		cout << m_name << " : " << m_age << endl;
	}

public:
	string m_name="Tom";
	int m_age=5;

};

class Student:public Person//公有继承
{
public:
	Student(string name, int age) :m_name(name), m_age(age) {}

};

void test01()
{
	Person p;
	p.Print();

	Student s;
	s.Print();
}

void test02()
{

}

void main()
{
	test01();

	system("pause");
}

 公有继承:

#include<iostream>
using namespace std;

//父类的构造和析构到底能否调用?
class A
{
public:
	A(int a = 0) :m_a(a) { cout << "A()" << endl; }

public:
	void func()
	{
		cout << "this is A::func()" << endl;
	}
protected:
	void show()
	{
		cout << "this is A::show()" << endl;
	}
private:
	void print()
	{
		cout << "this is A::print()" << endl;
	}
private:
	int m_a;
};

class B :public A//除了父类的构造和父类的析构其它的可以继承
{
public:
	B(int b = 0) :m_b(b) { cout << "B()" << endl; }

	void list()
	{
		func();
		show();//公有继承父类的保护
		//print();//父类的的私有方法在子类中不可以访问
	}
private:
	int m_b;
};

void test01()
{
	cout << sizeof(A) << endl;
	cout << sizeof(B) << endl;
}

void test02()
{
	B b;
	//b.func();//只能调公有继承下的父类的公有
	b.list();
}

void main()
{
	test02();
	system("pause");
}

私有继承:

#include<iostream>
using namespace std;

//父类的构造和析构到底能否调用?
class A
{
public:
	A(int a = 0) :m_a(a) { cout << "A()" << endl; }

protected:
	void func()
	{
		cout << "this is A::func()" << endl;
	}

	void show()
	{
		cout << "this is A::show()" << endl;
	}
private:
	void print()
	{
		cout << "this is A::print()" << endl;
	}
private:
	int m_a;
};

class B :private A//除了父类的构造和父类的析构其它的可以继承
{
public:
	B(int b = 0) :m_b(b) { cout << "B()" << endl; }

	void list()
	{
		func();
		show();
		//print();//父类的的私有方法在子类中不可以访问
	}
private:
	int m_b;
};

void test01()
{
	cout << sizeof(A) << endl;
	cout << sizeof(B) << endl;
}

void test02()
{
	B b;
	//b.func();//只能调公有继承下的父类的公有
	//b.func();//私有继承父类公有变成子类的私有
	b.list();
}

class C :public B
{
public:
	void fun1()
	{
		//func();//私有继承变成了儿子的私有成员,孙子类不可以访问
	}
};

void main()
{
	test02();
	system("pause");
}

保护继承:

#include<iostream>
using namespace std;

//父类的构造和析构到底能否调用?
class A
{
public:
	A(int a = 0) :m_a(a) { cout << "A()" << endl; }

protected:
	void func()
	{
		cout << "this is A::func()" << endl;
	}

	void show()
	{
		cout << "this is A::show()" << endl;
	}
private:
	void print()
	{
		cout << "this is A::print()" << endl;
	}
private:
	int m_a;
};

class B :protected A//除了父类的构造和父类的析构其它的可以继承
{
public:
	B(int b = 0) :m_b(b) { cout << "B()" << endl; }

	void list()
	{
		func();
		show();
		//print();//父类的的私有方法在子类中不可以访问
	}
private:
	int m_b;
};

void test01()
{
	cout << sizeof(A) << endl;
	cout << sizeof(B) << endl;
}

void test02()
{
	B b;
	//b.func();//只能调公有继承下的父类的公有
	//b.func();//保护继承父类公有变成子类的保护
	b.list();
}

void main()
{
	test02();
	system("pause");
}

struct以及class的默认继承方式:

#include<iostream>
using namespace std;

class Base
{
public:
	void show()
	{
		fun();
	}

private:
	void fun()
	{
		cout << "this is fun" << endl;
	}
};

//class D: Base//class不写继承属性时默认为私有继承
struct D: Base//struct 不写继承属性时默认为公有继承
{
	
};

void test01()
{
	D d;
	d.show();
}

void main()
{	
	system("pause");
}

使用private继承的目的是为了终止孙子类对爷爷类的继承:

#include<iostream>
using namespace std;

class Base
{
public:
	void show()
	{
		cout << "this is Base::show()" << endl;
	}
};

class Son :private Base
{
public:
	void sonfun()
	{
		show();
	}
};

class GreadSon :public Son//私有继承的目的终止对于Base的继承
{
public:
	void gsonfun()
	{
		//show();//不能访问其父类的私有
	}

};

继承中的构造和析构顺序:先构造父类再构造子类,先析构子类再析构父类,只产生一个对象,

赋值兼容规则:1.子类对象可以直接给父类对象赋值;2.子类对象的地址可以给父类的指针赋值;

3.子类的对象可以给父类的引用赋值;这是因为子类内部包含了完整的父类成员。但是反之不能父类给子类赋值。

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a = 0) :m_a(a)
	{
		cout << "Base::Base()" << endl;
	}

	~Base()
	{
		cout << "Base::~Base()" << endl;
	}

private:
	int m_a;
	int m_b;
	int m_c;
};

class D:public Base
{
public:
	D(int b = 0) :m_b(b)
	{
		cout << "D::D()" << endl;
	}

	~D()
	{
		cout << "D::~D()" << endl;
	}

private:
	int m_b;
};

void test01()
{
	D d;//先构造父类再构造子类,先析构子类再析构父类,只产生一个对象
	//赋值兼容规则
	Base b;
	b = d;//1.子类对象可以直接给父类对象赋值
	Base* pb = &d;//2.子类对象的地址可以给父类的指针赋值
	Base& refb = d;//3.子类的对象可以给父类的引用赋值
	//因为子类内部包含了完整的父类成员。但是反之不能父类给子类赋值
}

void main()
{
	test01();
	system("pause");
}

重点:在没有进行虚函数重写时,子类对象给父类对象赋值或者用父类指针或引用指向子类对象,调用父子类同名成员都是调的父类的。

子类没有的成员就会调父类,子类有就调子类。

同名隐藏,子类会隐藏继承下来的所有与父类同名的函数(包括重载)也叫重定义。隐藏的情况下可以使用作用域限定符来调父类的成员。

1. 在继承体系中基类和派生类都有独立的作用域。
2. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
3. 注意在实际中在继承体系里面最好不要定义同名的成员。

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a = 0) :m_a(a)
	{
		//cout << "Base::Base()" << endl;
	}

	~Base()
	{
		//cout << "Base::~Base()" << endl;
	}

	void fun()
	{
		cout << "this is Base::fun" << endl;
	}

	void fun(int a)
	{
		cout << "this is Base::fun(int)" << endl;
	}


	void list()
	{
		cout << "this is Base::list()" << endl;
	}

private:
	int m_a;
	int m_b;
	int m_c;
};

class D:public Base
{
public:
	D(int b = 0) :m_b(b)
	{
		//cout << "D::D()" << endl;
	}

	~D()
	{
		//cout << "D::~D()" << endl;
	}
	void fun()
	{
		cout << "this is D::fun()" << endl;
	}

	void fun(int a)
	{
		cout << "this is D::fun(int)" << endl;
	}

	void show()
	{
		cout << "this is D::show()" << endl;
	}

private:
	int m_b;
};

void test01()
{
	//都是调用父类的fun()
	Base b;
	D d;
	b = d;
	b.fun();

	Base* pb = &d;
	pb->fun();

	Base& refb = d;
	refb.fun();
}

void test02()
{
	//不能通过父类指针调子类自己的成员,指针和引用是类的切片过程
	D d;
	Base* pb = &d;
	//pb->show();
}

void test03()
{
	D d;
	d.fun();//调子类自己的方法。
	d.list();//子类没有则调用父类的方法
}

void test04()
{
	//同名隐藏,子类会隐藏继承下来的所有与父类同名的函数(包括重载) 
	D d;
	d.fun();
	d.fun(1);
	d.Base::fun(1);//明确调父类
	d.Base::fun();//明确调父类
}

void main()
{
	test03();
	system("pause");
}

隐藏例子:

#include<iostream>
using namespace std;
class Person
{
protected:
	string _name = "小李子"; // 姓名
	int _num = 111; // 身份证号
};
class Student : public Person
{
public:
	void Print()
	{
		cout << " 姓名:" << _name << endl;
		cout << " 身份证号:" << Person::_num << endl;
		cout << " 学号:" << _num << endl;
	}
protected:
	int _num = 999; // 学号
};
void Test()
{
	Student s1;
	s1.Print();
};

void main()
{
	Student s;
	s.Print();
	system("pause");
}
#include<iostream>
using namespace std;

class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};
class B : public A
{
public:
	void fun(int i)
	{
	A::fun();
	cout << "func(int i)->" << i << endl;
	}
};
void Test()
{
	B b;
	//b.fun();//无法直接调父类无参,被隐藏了
	b.fun(10);
	b.A::fun();
}

void main()
{
	Test();
	system("pause");
}

重点:父类如果没有提供默认构造函数,则必须在子类的构造函数初始化列表显式调用。

派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

比较特殊:子类对象赋值时,如果使用类默认提供的赋值运算函数,它实际上会调用父类的赋值运算函数,但是当我们自己写赋值运算符重载时,就可以决定是否在子类的赋值运算符内部调用父类的赋值运算符重载,如果不调用就不执行父类的赋值运算符重载。

派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。派生类对象析构清理先调用派生类析构再调基类的析构。
派生类对象初始化先调用基类构造再调派生类构造。

构造函数:

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a) :m_a(a)
	{
		cout << "Base::Base()" << endl;
	}

	~Base()
	{
		cout << "Base::~Base()" << endl;
	}


private:
	int m_a;
};

class D:public Base
{
public:
	//父类如果没有提供默认构造函数,则必须在子类的构造函数初始化列表显式调用
	D(int b = 0) :m_b(b),Base(b)
	{
		cout << "D::D()" << endl;
	}

	~D()
	{
		cout << "D::~D()" << endl;
	}
	
private:
	int m_b;
};

void test01()
{
	D d;
}

void main()
{
	test01();
	system("pause");
}

 拷贝构造函数:

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a) :m_a(a)
	{
		cout << "Base::Base()" << endl;
	}

	Base(const Base& b)
	{
		cout << "A拷贝构造" << endl;
	}

	~Base()
	{
		cout << "Base::~Base()" << endl;
	}


private:
	int m_a;
};

class D :public Base
{
public:
	//父类如果没有提供默认构造函数,则必须在子类的构造函数初始化列表显式调用
	D(int b = 0) :m_b(b), Base(b)
	{
		cout << "D::D()" << endl;
	}

	D(const D& d):Base(d)
	{
		m_b = d.m_b;
		cout << "B拷贝构造" << endl;
	}

	~D()
	{
		cout << "D::~D()" << endl;
	}

private:
	int m_b;
};

void test01()
{
	D d1;
	D d2 = d1;
}

void main()
{
	test01();
	system("pause");
}
#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a) :m_a(a)
	{
		cout << "Base::Base()" << endl;
	}

	Base(const Base& b)
	{
		cout << "A拷贝构造" << endl;
	}

	~Base()
	{
		cout << "Base::~Base()" << endl;
	}


private:
	int m_a;
};

class D :public Base
{
public:
	//父类如果没有提供默认构造函数,则必须在子类的构造函数初始化列表显式调用
	D(int b = 0) :m_b(b), Base(b)
	{
		cout << "D::D()" << endl;
	}

	D(const D& d):Base(d)
	{
		m_b = d.m_b;
		cout << "B拷贝构造" << endl;
	}

	~D()
	{
		cout << "D::~D()" << endl;
	}

private:
	int m_b;
};

void test01()
{
	D d1;
	D d2 = d1;
}

void main()
{
	test01();
	system("pause");
}

赋值运算符重载:
 

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a) :m_a(a)
	{
		cout << "Base::Base()" << endl;
	}

	Base(const Base& b)
	{
		cout << "Base拷贝构造" << endl;
	}
	Base& operator=(const Base& b)
	{
		if (this != &b)
		{
			this->m_a = b.m_a;
		}
		cout << "Base::operator=" << endl;
		return *this;
	}

	~Base()
	{
		cout << "Base::~Base()" << endl;
	}


private:
	int m_a;
};

class D :public Base
{
public:
	//父类如果没有提供默认构造函数,则必须在子类的构造函数初始化列表显式调用
	D(int b = 0) :m_b(b), Base(b)
	{
		cout << "D::D()" << endl;
	}

	D(const D& d) :Base(d)
	{
		m_b = d.m_b;
		cout << "D拷贝构造" << endl;
	}

	//子类对象赋值时,子类有赋值运算赋重载则调子类,没有则调父类的,即默认调父类的,没有的时候一定会调用父类的
	D& operator=(const D& d)
	{
		if (this != &d)
		{
			Base::operator=(d);//注意这里不调用的话就是只给子类的成员变量赋值,不会给父类部分赋值
			this->m_b = d.m_b;
		}
		cout << "D::operator=" << endl;
		return *this;
	}

	~D()
	{
		cout << "D::~D()" << endl;
	}

private:
	int m_b;
};

void test01()
{
	D d1;
	D d2(10);
	d2 = d1;
}

void main()
{
	test01();
	system("pause");
}

多继承时,构造顺序只和继承顺序以及定义成员变量的顺序有关,与初始化列表的顺序无关。

(这里先按照继承顺序构造父类,然后按照定义顺序构造父类型的成员变量,接着初始化子类自己的成员变量,然后构造子类)

#include<iostream>
using namespace std;

class A
{
public:
	A(int a) :m_a(0)
	{
		cout << "A::A()" << endl;
	}

	~A()
	{
		cout << "A:~A()" << endl;
	}

private:
	int m_a;
};

class B
{
public:
	B(int b) :m_b(0)
	{
		cout << "B::B()" << endl;
	}

	~B()
	{
		cout << "B:~B()" << endl;
	}

private:
	int m_b;
};

class C
{
public:
	C(int c) :m_c(0)
	{
		cout << "C::C()" << endl;
	}

	~C()
	{
		cout << "C:~C()" << endl;
	}

private:
	int m_c;
};

class D :public A, public B, public C//构造顺序只与继承顺序有关,与初始化列表顺序无关
{
public:
	//这里先按照继承顺序构造父类,然后按照定义顺序构造父类型的成员变量,接着初始化子类自己的成员变量,然后构造子类
	D() :C(1), B(2), A(2), a(10), b(20),c(30)//初始化列表最卑微,初始化和构造的顺序与它无关
	{
		cout << "D::D()" << endl;
	}

	~D()
	{
		cout << "D:~D()" << endl;
	}

private:
	int m_d;
	B b;
	A a;
	C c;
};

void test01()
{
	D d;
}

void main()
{
	test01();
	system("pause");
}
#include<iostream>
using namespace std;

class A
{
public:
	A(int a) :m_a(0)
	{
		cout << "A::A()" << endl;
	}

	~A()
	{
		cout << "A:~A()" << endl;
	}

private:
	int m_a;
};

class B
{
public:
	B(int b) :m_b(0)
	{
		cout << "B::B()" << endl;
	}

	~B()
	{
		cout << "B:~B()" << endl;
	}

private:
	int m_b;
};

class C
{
public:
	C(int c) :m_c(0)
	{
		cout << "C::C()" << endl;
	}

	~C()
	{
		cout << "C:~C()" << endl;
	}

private:
	int m_c;
};

class D :public A, public B, public C//构造顺序只与继承顺序有关,与初始化列表顺序无关
{
public:
	//这里先按照继承顺序构造父类,然后按照定义顺序构造父类型的成员变量,接着初始化子类自己的成员变量,然后构造子类
	D() :C(1), B(2), A(2), a(10), b(20),c(30)//初始化列表最卑微,初始化和构造的顺序与它无关
	{
		cout << "D::D()" << endl;
	}

	~D()
	{
		cout << "D:~D()" << endl;
	}

private:
	int m_d;
	B b;
	A a;
	C c;
};

void test01()
{
	D d;
}

void main()
{
	test01();
	system("pause");
}

       友元这种关系不具有继承性性,父类的友元不能访问子类的私有成员,即父类的友元不是子类的友元,要想访问子类的私有成员,也要将函数或者类声明为子类的友元。

#include<iostream>
using namespace std;
class Student;
class Person
{
public:
	friend void Display(const Person& p, const Student& s);
protected:
	string _name="ABC"; // 姓名
};
class Student : public Person
{
	//friend void Display(const Person& p, const Student& s);
protected:
	int _stuNum=1000; // 学号
};

void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}

void main()
{
	Person p;
	Student s;
	Display(p, s);
	system("pause");
}

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 ,即只保存这一份数据。

#include<iostream>
using namespace std;

class Person
{
public:
	Person() { ++_count; }
protected:
	string _name; // 姓名
public:
	static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
class Graduate : public Student
{
protected:
	string _seminarCourse; // 研究科目
};
void TestPerson()
{
	Student s1;
	Student s2;
	Student s3;
	Graduate s4;
	cout << " 人数 :" << Person::_count << endl;
	Student::_count = 0;
	cout << " 人数 :" << Person::_count << endl;
}

void main()
{
	TestPerson();
	system("pause");
}

菱形继承:一个爷爷类被两个儿子类继承,然后一个孙在类同时继承了这两个儿子类。造成的问题是产生二义性,数据冗余浪费空间,因为对同一份数据需要维护两个空间,使用时会发生混乱。

解决方式:两个儿子类虚继承爷爷类。这时爷爷类叫做虚基类。虚继承通过虚基类指针,指向虚基类表来获取偏移量,从而找到虚基类中的唯一数据,减少内存消耗。两个派生类有各自的虚基类指针,指向派生类各自的虚基类表来获取偏移量,从而找到虚基类中的唯一数据,减少内存消耗。

需要注意的是,虚拟继承不要在其他地方去使用。

通过内存可以观察到:

通过开发人员命令提示符工具也可以观察(其它的例子,原理一样):

#include<iostream>
using namespace std;

class A
{
public:
	int m_a=1;
};

class B: virtual public A//虚拟继承解决数据重复存储问题,被虚拟继承的类叫做虚基类
{
public:
	int m_b=2;
};

class C: virtual public A//虚拟继承解决数据重复存储问题
{
public:
	int m_c=3;
};

class D :public B, public C
{
public:
	int m_d=4;
};

void test01()
{
	D d;
	cout << d.m_b << endl;
	cout << d.m_d << endl;
	cout << d.m_c << endl;
	//d.B::m_a = 10;//维护两个空间,使用时会发生混乱
	//d.C::m_a = 20;
    //cout << d.m_a << endl;//菱形继承,产生二义性,数据冗余浪费空间
	cout << d.B::m_a << endl;
	cout << d.C::m_a << endl;
	//cout <<d.A::m_a << endl;//访问不合理

	cout << &d << endl;
	cout << &(d.B::m_a) << endl;//地址不同浪费空间
	cout << &(d.C::m_a) << endl;
}

void main()
{
	test01();
	system("pause");
}

 在菱形继承下,如果要拿孙子类对象给儿子类对象赋值,显然就要从孙子类对象中找到儿子类各自的数据,这样也就提现了使用虚继承更加合理和准确。

#include<iostream>
using namespace std;

class A
{
public:
	int _a=1;
};
class B : public A
//class B : virtual public A
{
public:
	int _b=2;
	void setA()
	{
		_a = 10;
	}
};
class C : public A
//class C : virtual public A
{
public:
	int _c=3;
};
class D : public B, public C
{
public:
	int _d=4;
};
int main()
{
	D d;
    C c = d;
	cout << c._a << endl;
	B b = d;
	cout << b._a << endl;
	system("pause");
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值