c++的继承

继承是面向对象复用的重要手段.通过继承定义一个类,继承是类型之间的关系建模,共享公有的东西,实现各自本质不同的东西.


#include<iostream>
#include<stdlib.h>
#include<string.h>


using namespace std;
class Person
{
private:
	string _name;


public:
	void show()
	{
		cout << "Person" << endl;
	}
};
class Student:public Person
{
public:
	 void display()
	{
		cout << "Student" << endl;
	}
private:
	int _stunum;;
};
int main()
{
	Student s;
	system("pause");
	return 0;

}

继承是一种复用手段,在继承关系里基类的成员类的成员派生类的成员,由此达到复用目的.


总结:

1.基类的私有成员在派生类中是不能被访问的,如果一些基类成员不想被基类对象直接访问,但需要在派生类中能访问,就定义为保护成员,可以看出保护成员限定符是因为继承才出现.

2.punlic继承是一个接口继承,保持is-a(每一个子类都是一个父类)原则,每个父类可用的成员对子类也是可用的,每个子类对象也都是一个父类对象.

3.protected/private继承是一个实现继承,基类的部分成员并未完全称为子类接口的一部分,是has-a(每一个子类中都有一个父类)的关系原则.所以非特殊情况下是不会用这两种继承关系,绝大多是情况下都是使用公有继承.

4.不管哪种继承方式,在派生内部都可以访问基类的公有成员和保护成员,但是基类的私有成员存在但是在子类中不可见(不能访问)

5.使用关键字class时默认的继承方式时private,使用struct时默认的继承方式时public,不过最好显示的写出继承方式.

6.在实际运用中一般都是用public继承,极少场景下才会使用protected/private继承.

继承与转换--赋值兼容规则--public继承

1.punlic继承时子类可以赋值给父类--切片

2.子类对象不能赋值给子类对象

3.父类的指针/引用可以指向子类对象

4.子类的指针/引用不可以指向父类对象(可以通过强制类型转换完成)

(这种情况会出现问题就是越界访问)

例:

#include<iostream>
#include<stdlib.h>
#include<string.h>


using namespace std;
class Person
{
protected:
	string _name;


public:
	void show()
	{
		cout << "Person" << endl;
	}
};
class Student:public Person
{
public:
	 void display()
	{
		cout << "Student" << endl;
	}
private:
	int _num;;
};
int main()
{
	Person p;
	Student s;
	p=s;            //子类对象可以赋值给父类对象(切片/切割)
	//s=p;         // 父类对象不能赋值给子类对象
	//父类的指针/引用可以指向子类对象
	Person *p1 = &s;
	Person &p2 = s;
	//子类的指针/引用不可以指向父类对象(可以通过强制类型转换完成)
	Student* s1 = (Student*)&p;
	Student& s2 = (Student&)p;
	system("pause");
	return 0;

}


继承体系中的作用域:

1.在继承体系中基类和派生类都有独立的作用域

2.子类和父类中有同名成员(同名成员函数和同名成员变量),子类成员将屏蔽父类成员的直接访问(子类成员函数中,可以使用 基类::基类成员 访问)--(隐藏)重定义

3.注:在实际继承体系中最好不要定义同名的成员

例:(同名成员变量)

#include<iostream>
#include<stdlib.h>
#include<string.h>


using namespace std;
class Person
{
public:
	string _name;


public:
	void show()
	{
		cout << "Person" << endl;
	}
};
class Student:public Person
{
public:
	 void display()
	{
		cout << "Student" << endl;
	}
public:
	int _num;;
};
int main()
{
	Person p;
	p._name = 10;
	Student s;
	cout << sizeof(s) << endl;
	s._name = 20;              //访问的是子类的_name(这就是重定义/隐藏)
	s.Person::_name = 200;//指定作用域是可以访问到父类的_name
	system("pause");
	return 0;
}

例:(同名成员函数)

#include<iostream>
#include<stdlib.h>
#include<string.h>


using namespace std;
class Person
{
public:
	
	void show()
	{
		cout << "Person show()" << endl;
	}
	void f1(int i)
	{
		cout << "Student shows()" << endl;
	}
	string _name;
};
class Student:public Person
{
public:
	 void show()
	{
		cout << "Student shows()" << endl;
	}
	 void f1()
	 {
		 cout << "Student shows()" << endl;
	 }
	int _num;;
};
int main()
{
	Person p;
	Student s;
	s.show();//两个show构成重定义/隐藏;
	s.f1(); //两个f1构成重定义/隐藏(虽然他们的参数不同但是名字是相同的)
	system("pause");
	return 0;

}


继承关系里面,在派生类中如果没有显示的定义这6个成员函数编译器会默认合成这6个成员函数


#include<iostream>
#include<stdlib.h>
#include<string.h>


using namespace std;
class Person
{
public:
	Person()
		:_name("name")
	{
		cout << "Person" << endl;
	}
	Person(const Person& p)
		:_name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)
	{
		cout << "Person& operator=(const Person& p)" << endl;
		if (this != &p)
		{
			_name = p._name;
		}
		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name;
};

class Student:public Person
{
public:
	//构造
	Student()
		:Person()//name是父类的成员所以要调用父类的构造函数
		,_num(0)
	{
		cout << "Student()" << endl;
	}
	//拷贝构造
	Student(const Student& s)
		:Person(s)
		,_num(s._num)
	{
		cout << "Student(const Student& s)" << endl;
	}
	Student& operator=(const Student& s)
	{
		cout << "Student& operator=(const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator=(s);
			_num = s._num;
		}
		return *this;
	}
	~Student()
	{
		Person::~Person();
		cout << "~Student()" << endl;
	}
protected:
	int _num;;
};
int main()
{
	Student s1;
	Student s2(s1);
	Student s3;
	s1 = s3;
	Person p;
	s1.~Student(); 
	p.~Person();
	system("pause");
	return 0;

}

析构函数比较特殊,因为子类的析构函数跟父类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理


子类的构造是先构造父类再构造子类

子类的析构实现析构子类再析构父类

实现一个不能被继承的类:

方法一:

#include<iostream>
#include<stdlib.h>
#include<string.h>

using namespace std;
class AA
{
private:
	AA()
	{}
protected:
	int _aa;
};
class BB: public AA
{

protected:
	int _bb;
};

缺点:AA 也不能在类外用因为他们是私有的

方法二:

.h
#include<iostream>
#include<stdlib.h>
#include<string.h>

using namespace std;
class AA
{
private:
	AA()
	{}
public:
	static AA* GetAA()
	{
		return new AA;
	}
	
protected:
	int _aa;
};
class BB: public AA
{

protected:
	int _bb;
};
.cpp
#include"test.h"
int main()
{
	AA* p1 = AA::GetAA();
	system("pause");
	return 0;

}
实现一个只能在栈上生成对象的类:
class BB
{
public:
	static BB GetBB()
	{
		return   BB();
	}
private:
	BB(int b=0)
		:_bb(b)
	{}
	
public:
 int _bb;
};

实现一个只能在堆上生成对象的类:(为防止在堆上生成成员再拷贝构造一个栈上的所以要做到防拷贝)

方法一:

class BB
{
public:
	static BB& GetBB()
	{
		return  *(new BB);
	}
	
private:
	BB(int b=0)
		:_bb(b)
	{}
	
       //类的防拷贝
	//1.之声明不定义
	//2.要声明为私有(防止别人在类外定义)
	BB(const BB& bb);
	BB& operator=(const BB& bb);
public: int _bb;};

方法二:

class BB
{
public:
	static BB* GetBB()
	{
		return  new BB;
	}
	
private:
	BB(int b=0)
		:_bb(b)
	{}
	
        //类的防拷贝
	//1.之声明不定义
	//2.要声明为私有(防止别人在类外定义)
	BB(const BB& bb);
	BB& operator=(const BB& bb);
public:
int _bb;};
实现一个不能在堆上生成对象的类
class BB
{
private:
	//这样防止了在堆上生成对象
	void* operator new(size_t size);
	void  operator delete(void* p);

 int _bb;
};


单继承&多继承



#include<iostream>
#include<stdlib.h>
#include<string.h>

using namespace std;
class A
{
public:
	int _name;
};
class B :public A
{
public:
	int _num;
};
class C :public A
{

public:
	int _id;
};
class D :public B, public C
{
public:
	int _course;
};
int main()
{
	D d;
	d ._name=1;
	d ._num= 2;
	d._id = 3;
	d._course = 4;
	system("pause");
	return 0;
}


C中有两份A成员所以菱形继承存在二义性和数据冗余;

菱形虚继承


#include<iostream>
#include<stdlib.h>
#include<string.h>

using namespace std;
class A
{
public:
	int  _name;
};
class B :virtual public A
{
public:
	int _num;
};
class C :virtual public A
{
public:
	int _id;
};
class D :public B, public C
{
public:
	int _course;
};
int main()
{
	D d;
	d._name = 1;
	d._num = 2;
	d._id = 3;
	d._course = 4;
	system("pause");
	return 0;
}

虚函数&多态

虚函数——类的成员函数前面加关键字virtual,则这个成员函数就是虚函数。

虚函数重写——当在子类的定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写了(也称覆盖)了父类的这个虚函数。

#include<iostream>
#include<stdlib.h>
#include<string.h>

using namespace std;
class A
{
public:
	virtual void f1()
	{
		cout << "A" << endl;
	}
	int  _name;
};
class B : public A
{
public:
	virtual void f1()
	{
		cout << "B" << endl;
	}
	int _num;
};
void fun(A& m)
{
	m.f1();
}
void fun1(A* m)
{
	m->f1();
}
int main()
{
	
	//构成多态的条件--跟对象有关
	//1.函数(fun/fun1)的参数必须是父类的指针/引用
	//2.函数(fun/fun1)里调用的函数(f1)必须是虚函数的重写
	A a;
	B b;
	fun(a);
	fun(b);
	A c;
	B d;
	fun1(&c);
	fun1(&d);
	system("pause");
	return 0;
}
#include<iostream>
#include<stdlib.h>
#include<string.h>

using namespace std;
class A
{
public:
	 void f1()
	{
		cout << "A" << endl;
	}
	int  _name;
};
class B : public A
{
public:
	 void f1()
	{
		cout << "B" << endl;
	}
	int _num;
};
void fun(A& m)
{
	m.f1();
}


int main()
{
	//不构成多态的函数--跟类型有关
	
	A a;
	B b;
	fun(a);
	fun(b);
	
	system("pause");
	return 0;
}


总结:

1.子类重写父类的虚函数实现多态,要求函数名,参数列表,返回值完全相同。

2.父类定义了虚函数,在子类中该函数始终保持虚函数特性。

3.只有类的成员函数才能定义为虚函数。

4.静态成员函数不能定义为虚函数。

5.如果在类外面定义了虚函数,只能是声明时加virtual,类外定义虚函数时不能加virtual。

6.构造函数不能是虚函数,虽然可以将operator=定义为虚函数,但是最好不要把operator=定义为虚函数,因为使用时容易引起混淆。

7.不要在构造函数和析构函数中调用虚函数,在构造和析构函数中对象是不完整的容易引发未定义行为。

8.最好把父类的析构函数声明为虚函数(原因:析构函数比较特殊,因为子类的析构函数跟父类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理(参考上面合成默认函数)


纯虚函数

在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化处对象

class A
{
	virtual void show() = 0;//纯虚函数
protected:
	string _name;//名字
};
class B :public A
{};
友元与继承

有缘关系不能继承,也就是说基类友元不能访问子类私有和保护成员。

友元与静态成员

基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论父类中出多少个类,都只有一个static成员

.h
#include<iostream>
#include<stdlib.h>
#include<string.h>

using namespace std;
class AA
{
public:
	AA()
	{
		++_count;
	}
protected:
	string _name;
public:
	static int _count;
};
int AA::_count = 0;
class BB : public AA
{
protected:
	int _stunum;
};
class CC:public BB
{
protected:
	string _course;
};
.cpp
#include"test.h"
int main()
{
	AA a1;
	AA a2;
	AA a3;
	CC c;
	cout << "人数" << AA::_count << endl;
	AA::_count = 0;
	cout << "人数" << AA::_count << endl;
	system("pause");
	return 0;

}











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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值