面向对象基础

本文详细介绍了面向对象编程中的继承概念,包括公有、私有和保护继承的特性及其对基类和派生类成员的可见性影响。通过实例展示了单继承、多继承以及虚继承的实现方式,并解释了构造函数和析构函数的调用顺序。此外,还探讨了虚函数、纯虚函数和虚析构函数的作用,以及在类层次结构中如何正确使用它们。最后,讨论了虚函数在不同继承和指针类型场景下的行为差异。
摘要由CSDN通过智能技术生成

继承

1、继承的写法:

子类:继承方式  父类

{

}

继承方式:

public 表示公有继承;

private 表示私有继承

protected 表示保护继承;

1. 公有继承(public)

公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。

2. 私有继承(private)

私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。

3. 保护继承(protected)

保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。

下面列出三种不同的继承方式的基类特性和派生类特性。

public

protected

private

公有继承

public

protected

不可见

私有继承

private

private

不可见

保护继承

protected

protected

不可见

为了进一步理解三种不同的继承方式在其成员的可见性方面的区别,下面从三种不同角度进行讨论。

公有方式

(1) 基类成员对其对象的可见性:

公有成员可见,其他不可见。这里保护成员同于私有成员。

(2) 基类成员对派生类的可见性:

公有成员和保护成员可见,而私有成员不可见。这里保护成员同于公有成员。

(3) 基类成员对派生类对象的可见性:

公有成员可见,其他成员不可见。

所以,在公有继承时,派生类的对象可以访问基类中的公有成员;派生类的成员函数可以访问基类中的公有成员和保护成员。这里,一定要区分清楚派生类的对象和派生类中的成员函数对基类的访问是不同的。

私有方式

(1) 基类成员对其对象的可见性:

公有成员可见,其他成员不可见。

(2) 基类成员对派生类的可见性:

公有成员和保护成员是可见的,而私有成员是不可见的。

(3) 基类成员对派生类对象的可见性:

所有成员都是不可见的。

所以,在私有继承时,基类的成员只能由派生类中的成员函数访问,而且无法再往下继承。

保护方式

这种继承方式与私有继承方式的情况相同。两者的区别仅在于对派生类的成员而言,对基类成员有不同的可见性。

上述所说的可见性也就是可访问性。关于可访问性还有另的一种说法。这种规则中,称派生类的对象对基类访问为水平访问,称派生类的派生类对基类的访问为垂直访问。

单继承的定义:从一个基类派生的继承称为单继承;

 例子:

#include <iostream>
#include <string>

using namespace std;

class MM
{
public:
	MM() {
		cout << "MM" << endl;
	}

	MM(string name, int age) :name(name), age(age) {
		cout << "父类的带参的构造函数" << endl;
	}
	void print() {
		cout << "MM" << name << ":" << age << endl;
	}
protected:
	string name;
	int age;
private:
	int num;
};

//继承子类写法
//public:最低权限    protected    private:最高
class Boy : public MM
{
public:

	Boy(){
		cout << "子类无参构造函数" << endl;
	}
		Boy(string name, int age) :MM(name, age) {

		cout << "子类带参的构造函数" << endl;
	}
	//子类的构造函数,必须调用父类的构造函数
	void printBoy() {
		cout << "Boy" << name << ":" << age << endl;
		//cout << num << endl;//父类中的私有属性不可访问
	}
protected:
	//string name
	//int age
};

int main()
{
	MM mm("MM", 18);
	mm.print();
	Boy boy;//子类构造对象,必定会调用父类的构造函数
	Boy boy1("张三", 20);

	return 0;
}

构造顺序和析构顺序:

通常情况下:构造顺序和析构顺序是相反的,先构造后析构。

例子:

#include <iostream>

using namespace std;


class A  
{
public :
	A() {
		cout << "A";
	}
	~A() {
		cout << "A" ;
	}
private:

};

class B :public A
{
public:
	B() {
		cout << "B"<< endl;
	}
	~B() {//对象死亡的时候会自动调用析构函数
		cout << "B" << endl;
	};

private:

};


int main(void) 
{
	{//作用限定符
		A aObject;	//A
		B bObject;	//AB
		//AABBAA
	}

	{
		//类和对象,构造函数
		B *p = new B;//AB
		B bObject;//AB
		delete p;//BABA
	}
}

类的遗传性:

例子:

#include <iostream>

using namespace std;

class A 
{
public:
	A(int a) :a(a) {}
	int a;
};

class B:public A
{
public:
	B(int a,int b) :b(b),A(a){}
	//int a;
	int b;
};

class C :public B
{
public:
	C(int a, int b, int c) :B(a, b), c(c) {};
	//int a;
	//int b;
	int c;
};

class D :public C
{
public:
	D(int a, int b, int c,int d) :C(a, b,c), d(d) {};
	void print() {
		cout << a << ":" << b << ":" << c << ":" << d << endl;
	}
	//int a;
	//int b;
	//int c;
	int d;
};


int main(void) 
{
	D dObject(1, 2, 3, 4);
	dObject.print();
	return 0;
}

继承中的同名问题:

正常情况下:就近原则。

非正常情况下:

父类指针被子类初始化

1.没有virtual的情况下同名问题:看类型,不看对象

2.有virtual就是看对象

例子:

/*同名问题:
1.数据成员同名
2.成员函数同名
*/
#include <iostream>
#include <string>

using namespace std;

class MM 
{
public:
	MM(string name,int age):name(name),age(age) {}
	void print() 
	{
		cout << "MM " << endl;
		cout << age << endl;
	}
protected:
	string name;
	int age;
};

class Boy :public MM 
{
public:

	Boy(string name,int age,string mmName,int mmAge):name(name),age(age),MM(mmName,mmAge) {}
	void print() {
		//通常情况都是就近原则
		cout << name << ":" << age << endl;
		//类名限定
		cout << MM::name << ":" << MM::age << endl;
	}
protected:
	string name;
	int age;
};
int main(void) 
{
	Boy mm("boy", 18, "MM", 28);
	mm.print();

	//非正常情况
	//父类指针被子类对象初始化
	//没有virtual的情况下:看类型,不看对象
	//有virtual就是看对象
	MM *pMM = new Boy("MMBoy",19,"mm",29);
	pMM->print();
	return 0;
}

认识多继承:存在两个及以上的父类

权限和单继承一样

构造函数写法和单继承也是一样

例子:

#include <iostream>
#include <string>

using namespace std;

class MM
{
public:
	MM(string mmFname, string mmSname) :MMFname(mmFname), MMSname(mmSname) {};

protected:
	string MMFname;
	string MMSname;
};

class Boy
{
public:
	Boy(string boyFname, string boySname) :boyFname(boyFname), boySname(boySname) {};

protected:
	string boyFname;
	string boySname;
};

class Son:public MM,public Boy
{
public:
	Son(string boyFname, string boySname, string mmFname, string mmSname,string sSname)\
		:Boy(boyFname,boySname),MM(mmFname,mmSname),sSname(sSname),sFname(boyFname+mmFname){};

	void print() {
		cout << "MM" << MMFname + MMSname << endl;
		cout << "BOY" << boyFname + boySname << endl;
		cout << "SON" << sFname + sSname << endl;
	}

protected:
	string sFname;
	string sSname;
};

int main(void ) 
{
	Son *pSon = new Son("欧", "养蜂", "阳", "莉莉", "修");
	pSon->print();
	return 0;
}

虚继承:

虚继承中的构造函数的写法必须调用祖父的构造函数
并且当前类中的继承祖先类属性只和调用的祖父类的构造函数传参有关系

例子:

#include <iostream>
#include <string>

using namespace std;

class A 
{
public:
	A(int a):a(a) {}
protected:
	int a;
};

class B :public  virtual A 
{
public:
	B(int b) :A(b) {}
};
class C :public virtual A
{
public:
	C(int c) :A(c) {}

};

class D :public B,public C
{
public:
	//虚继承中的构造函数的写法必须调用祖父的构造函数
	//并且当前类中的继承祖先类属性只和调用的祖父类的构造函数传参有关系
	D(int d) :B(d),C(d),A(123) {}
	void print() {
		cout << B::a << endl;
		cout << C::a << endl;
		cout << a << endl;//默认的相同属性是祖先元素
	}
};
int main(void) 
{
	D d(1);
	d.print();

	return 0;
}

虚函数:用virtual修饰的函数(构造函数没有虚函数)

虚函数:用virtual修饰的函数(构造函数没有虚函数)
虚函数对于类的内存影响:
    在32位操作系统下无论多少个虚函数,只会增加四个字节
纯虚函数:
    抽象类:具有纯虚函数的类
    抽象类的特性:不能创建对象,但是可以创建对象指针
    抽象数据类型 abstract data type
虚析构函数:是因为会父类指针被子类对象初始化的话析构函数会出现问题

空类占用一个字节  内存标记。

#include <iostream>
#include <string>

using namespace std;
/*
虚函数:用virtual修饰的函数(构造函数没有虚函数)
虚函数对于类的内存影响:
	在32位操作系统下无论多少个虚函数,只会增加四个字节
纯虚函数:
	抽象类:具有纯虚函数的类
	抽象类的特性:不能创建对象,但是可以创建对象指针
	抽象数据类型 abstract data type
虚析构函数:是因为会父类指针被子类对象初始化的话析构函数会出现问题
*/
class M 
{

};

class Boy 
{
public:
	//虚函数表:函数指针用来操作虚函数
	virtual void print1() {
		cout << "虚函数1" << endl;
	}
	virtual void print2() {
		cout << "虚函数2" << endl;
	}
	virtual void print3() {
		cout << "虚函数3" << endl;
	}
	int age;
};
//ADT过程
class Stack 
{
public:
	//纯虚函数  子类创建对象 需要重写纯虚函数
	virtual void push(int element) = 0;
	virtual void pop() = 0;
	virtual int top() = 0;
	virtual bool empty() = 0;
};

class A 
{
public :
	virtual ~A() {
		cout << "父类析构函数" << endl;
	}
};

class B :public A
{
public:
	 ~B() {
		cout << "子类析构函数" << endl;
	}
};
//ADT过程
class ArrayStack :public Stack
{
	void push(int element) {}
	void pop() {}
	int top() { return 0; }
	bool empty() { return true; }
};
int main() 
{
//	Stack mystack;
	A *pA = new B;
	delete pA;
	return 0;
}

虚函数和继承:

正常对象和正常指针访问:都是就近原则
不正常情况
    1.不存在virtual  看指针类型

    2.存virtual 看对象

例子:

#include <iostream>
#include <string>

using namespace std;

class A 
{
public :
	void print() {
		cout << "A " << endl;
	}
};

class B :public A
{
public:
	virtual void print() {
		cout << "B " << endl;
	}
};

class C :public B
{
public:
	void print() {
		cout << "C " << endl;
	}
};

class D :public C
{
public:
	void print() {
		cout << "D " << endl;
	}
};
int main() 
{
	//正常对象和正常指针访问:都是就近原则
	//不正常情况
	//1.不存在virtual  看指针类型
	A* p = new B;
	p->print();
	//2.存virtual 看对象
	B *pb = new C;
	pb->print();

	C *pc = new D;
	pc->print();
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值