目录
Linux中的管道通信
编辑派生类的默认成员函数
继承
派生类的构造
隐藏
如何设计一个不能被继承的类
菱形继承
virtual
virtual是如何解决的
内存对象模型
继承和组合
继承
组合
多态
概念
多态的构成条件
虚函数的重写
Linux中的管道通信
派生类的默认成员函数
1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函 数,则必须在派生类构造函数的初始化列表阶段显示调用。
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类 对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
继承
class Person
{
public:
Person(const char* name = "peter")
: _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
{
Student(const char* name, int stuid)
:Person(name)
,_stuid(stuid)
{}
private:
int _stuid;
};
Person(name) 相当于构造了一个匿名对象。
派生类的构造 其中有一部分继承自父类 只能自动调默认的构造函数(编译器默认生成的,全缺省的,无参的)
class Student : public Person
{
public:
Student(const char* name, int stuid)
:Person(name)
,_stuid(stuid)
{
cout << "Student()" << endl;
}
Student(const Student& s)
:Person(s)
, _stuid(s._stuid)
{
cout << "Student(const Student& s)" << endl;
}
private:
int _stuid;
};
隐藏
子类和父类有同名函数会构成隐藏
Person& operator=(const Person& p)
{
cout << "Person operator=(const Person& p)" << endl;
if (this != &p)
_name = p._name;
return *this;
}
Student& operator=(const Student& s)
{
if (this != &s)
{
operator=(s);
_stuid = s._stuid;
cout << "Student& operator=(const Student& s)" << endl;
}
return *this;
}
子类隐藏了父类的operator=会引发堆栈溢出。
更正:
if (this != &s)
{
Person::operator=(s);
_stuid = s._stuid;
cout << "Student& operator=(const Student& s)" << endl;
}
子类和父类的析构函数同样构成隐藏。因为其名字会被编译器替换为destructor.
~Student()
{
//Person::~Person();
cout << "~Student()" << endl;
}
结束时会自动调用父类的析构函数,因为这样才能保证先析构子类在析构父类。
如何设计一个不能被继承的类
class A
{
private:
A()
{}
};
class B : public A
{};
构造函数私有化后,B类无法实例化出对象。
子类在调用构造函数时必须先调用父类的。
友元关系不能被继承。
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一 个static成员实例 。
菱形继承
class Person
{
public :
string _name ; // 姓名
};
class Student : public Person
{
protected :
int _num ; //学号
};
class Teacher : public Person
{
protected :
int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
string _majorCourse ; // 主修课程
};
void Test ()
{
// 这样会有二义性无法明确知道访问的是哪一个
Assistant a ;
a._name = "peter";
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
}
virtual
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承 Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
class Student : virtual public Person
{
protected :
int _num ; //学号
};
class Teacher : virtual public Person
{
protected :
int _id ; // 职工编号
};
virtual是如何解决的
c++的缺陷有那些?
多继承->菱形继承->虚继承->底层结构的对象模型非常复杂,且有一定的效率损失。
什么事菱形继承?菱形继承存在的问题?如何解决?-> 虚继承 虚继承的解决原理是什么?
内存对象模型
对象在内存中是如何存的?
class A
{
public:
int _a;
};
// class B : public A
class B : virtual public A
{
public:
int _b;
};
// class C : public A
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
cout << sizeof(d) << endl;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
d._a = 6;
return 0;
}
继承和组合
都可以完成类层次的复用
public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
继承
继承是一种白箱复用,父类对子类基本是透明的,但在一定程度上破坏了父类的封装性。
class A{};
class B : class A
{};
例如:车和车品牌之间的关系
组合
组合是一种黑箱复用,c对d是不透明的,很好的保持了c的封装性。
class C {};
class D
{
C c;
};
例如轮胎和车的关系。
多态
概念
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。
多态的构成条件
1. 必须通过基类的指针或者引用调用虚函数
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
虚函数的重写
虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类 型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "买票-半价" << endl; }
/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后
基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用*/
/*void BuyTicket() { cout << "买票-半价" << endl; }*/
};
void Func(Person& p)
{ p.BuyTicket(); }
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}