C++/继承

1、基类和派生类对象赋值转换

        派生类对象可以赋值给,基类的对象 / 基类的指针 / 基类的引用。---切片(把派生类中父类那部分切片赋值过去)。

        继承天生支持向上转换,即 子类对象 ------> 父类对象/引用/指针。

class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
//protected:
	string _name = "peter"; // 姓名
	int _age = 18;  // 年龄
};

class Student : public Person
{
protected:
	int _stuid; // 学号
};

class Teacher : public Person
{
protected:
	int _jobid; // 工号
};

int main()
{

	Person p;
	Student s;

	// 赋值兼容转换(切割,切片)
	//p = s;
	Person p1 = s;
	Person& rp = s;
	rp._name = "张三";

	Person* ptrp = &s;
	ptrp->_name = "李四";

	return 0;
}

  下面进行在监视窗口进行展示:

        可以看到当:子类对象 ------> 父类引用/指针。rp和ptrp以及s指向同一空间,当改变_name时候,三者同时发生改变。

        父类对象无法赋值给子类对象。

2、 继承中的作用域

        a、在继承的体系结构中,基类派生类都有自己独立的作用域

        b、当子类和父类当中有同名的成员时,子类成员将屏蔽父类对同名成员的直接访问,也叫隐藏/重定义。

        c、函数名相同,构成成员函数的隐藏。

class Person
{
public:
	void fun()
	{
		cout << "Person::func()" << endl;
	}

protected:
	string _name = "小李子"; // 姓名
	int _num = 111; 	   // 身份证号
};

// 隐藏/重定义:子类和父类有同名成员,子类的成员隐藏了父类的成员
class Student : public Person
{
public:
	void fun()
	{
		cout << "Student::func()" << endl;
	}

	void Print()
	{
		cout << " 姓名:" << _name << endl;
		cout << _num << endl;
		cout << Person::_num << endl;
	}
protected:
	int _num = 999; // 学号
};

int main()
{
	Student s;
	s.Print();
	s.fun();
	s.Person::fun();

	return 0;
}

3 、派生类的默认成员函数

        a、对于派生类的构造函数/拷贝构造/赋值重载,必须调用基类的构造函数/拷贝构造/赋值重载去初始化基类的那一部分成员。

        b、如果基类没有默认构造函数的时候,则必须在派生类构造函数的初始化列表显示调用。

        c、对于析构函数,先调用子类的析构函数,再调用父类的析构函数。且编译器会对析构函数的名字进行特殊处理,处理成destrutor()。

class Person
{
public:
	//Person(const char* name = "peter")
	Person(const char* name)
		: _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;
		delete _pstr;
	}
protected:
	string _name; // 姓名

	string* _pstr = new string("111111111");
};

class Student : public Person
{
public:
	// 先父后子
	Student(const char* name = "张三", int id = 0)
		:Person(name)
		,_id(0)
	{}

	Student(const Student& s)
		:Person(s)
		,_id(s._id)
	{}


	Student& operator=(const Student& s)
	{
		if (this != &s)
		{
			Person::operator=(s);
			_id = s._id;
		}

		return *this;
	}

	~Student()
	{
		// 由于后面多态的原因(具体后面讲),析构函数的函数名被
		// 特殊处理了,统一处理成destructor

		// 显示调用父类析构,无法保证先子后父
		// 所以子类析构函数完成就,自定调用父类析构,这样就保证了先子后父
		//Person::~Person();

		cout << *_pstr << endl;
		delete _ptr;
	}
protected:
	int _id;

	int* _ptr = new int;
};

int main()
{
	Student s1;
	Student s2(s1);

	Student s3("李四", 1);
	s1 = s3;

	return 0;
}

4、继承与友元

        友元关系不能继承。

5、继承与静态成员

        基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。

// 静态成员属于父类和派生类
// 在派生类中不会单独拷贝一份,继承的使用权
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; // 研究科目
};

int main()
{
	Person p;
	Student s;

	cout << Person::_count << endl;

	cout << &p._name << endl;
	cout << &s._name << endl;
	cout << &p._count<< endl;
	cout << &s._count << endl;

	cout << &Person::_count << endl;
	cout << &Student::_count << endl;

	return 0;
}

6、菱形继承和菱形虚拟继承

        菱形继承会出现数据冗余(数据二义性问题)。

class A
{
public:
	int _a;
};

class B :  public A
{
public:
	int _b;
};

class C :  public A
{
public:
	int _c;
};

class D : public B, public C
{
public:
	int _d;
};


int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;

	D d1;

	return 0;
}

a、对于菱形继承,数据存储的结构模型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值