注意:给形参默认值:从左往右的顺序依次给,中间不能有空的
class Employee
{
char name[20];
Date birthday;
public:
Employee(char* a, int b, int c, int d):birthday(b, c, d)
{
strcpy_s(name, a);
}
};
参数初始化表的使用:
构造函数中初始化组合、继承时
兼容类型规则:
派生类的对象可以当作基类对象
这时只能使用基类成员
#include<iostream>
using namespace std;
class base {
public:
void fun() { cout << "base::fun()" << endl; }
};
class derive :public base {
public:
void fun() { cout << "derive::fun()" << endl; }
};
int main() {
base* p;
base b;
derive d;
p = &b;
p->fun();
p = &d;
p->fun();
return 0;
}
在这里使用一个基类指针,指向派生类的对象,却只能访问基类的同名函数。这一点在后文:同名隐藏也会提到。
访问控制规则
e.g. 私有继承下public和protected都变成了private,在派生类中可以访问,在类的对象中(类外)不可访问(后续会持续出现这一概念,不要混淆)
private在任何继承情况下都变成不可访问的,在派生类中都不可以访问
#include<iostream>
using namespace std;
class base {
private:
protected:
int num = 0;
public:
int sum = 0;
void fun() { cout << "base::fun()" << endl; }
};
class derive :private base {
public:
void showbase() { cout << num <<' '<<sum<< endl; }
void fun() { cout << "derive::fun()" << endl; }
};
int main()
{
derive d;
d.showbase();
}
输出:0 0
间接调用基类成员函数
法1:
不能使用基类函数,在派生类中写同名函数,并定义为调用基类的函数(类外不能使用继承来的private,但是类内可以)
class A
{
public:
void f1() {
cout << "b f1" << endl;
}
protected://在类外不可访问(相当于是private)在类内相当于是public
void f2()
{
cout << "bf2" << endl;
}
};
class B:public A {
public:
void f1() { A::f1(); cout << "Af1" << endl; }
void f2() { A::f2(); }
};
int main()
{
B a;
a.f1();
A* p = &a;
p->f1();
}
同样的,基类指针只能调用基类的同名函数。这里需要和后文虚函数区分。
法2:
对象在调用的时候说明是哪个类的同名函数
#include<iostream>
using namespace std;
class A
{
public:
void f1() {
cout << "af1" << endl;
}
public:
void f2()
{
cout << "af2" << endl;
}
};
class B :public A {
public:
void f1() { A::f1(); cout << "Bf1" << endl; }//执行顺序:先输出再调用A的f1
void f2() { A::f2(); cout << "Bf2" << endl; }
};
int main()
{
B a;
a.f1();
A* p = &a;
p->f1();
a.A::f2();
}
先执行完本函数,再去调用其他类的函数。
单一继承的构造函数和析构函数
基类的构造函数和析构函数都不能被继承
#include<iostream>
using namespace std;
class A
{
public:
A() { cout << "con A" << endl; }
~A() { cout << "des A" << endl; }
};
class B :public A {
A a;//组合
public:
B() { cout << "con B" << endl; }
~B() { cout << "des B" << endl; }
};
int main()
{
B b;
}
con A
con A
con B
des B
des A
des A
调用顺序:
先构造基类(如果在构造函数中没写,调用对应类的默认构造函数)
再构造派生类中的数据成员,
最后执行派生类对象的构造函数中的语句。
注意:
派生类的构造函数在没有写调用基类构造函数的情况下,运行时仍然需要调用基类的构造函数。
可以说,只要建立派生类对象,就需要调用基类构造函数。
但是析构函数:
向上文的程序中的main函数中添加:
A* p = new B();
输出:
con A
con A
con B
con A
con A
con B
des B
des A
des A
添加:
A* p = new B();
delete p;
输出
con A
con A
con B
con A
con A
con B
des A
des B
des A
des A
可以发现:基类指针即使是在指向派生类对象后,在delete的时候也只能调用基类的析构函数,导致派生类的数据会有剩余,这就需要后文的虚析构函数。
析构函数调用顺序和类的组合一样,与构造函数的顺序正好相反
区分 类的组合
#include<iostream>
using namespace std;
class A
{
int a;
public:
A() {cout << "consA" << endl; }
~A() { cout << "desA" << endl; }
//void showa() { cout << a << endl; }
};
class B
{
A a;
public:
B()
{
cout << "consB" << endl;
}
~B() { cout << "desB" << endl; }
//void showb() { cout << b << endl; }
};
int main()
{
B a;
}
构造的调用顺序和函数编写顺序无关,只与数据成员声明顺序有关
#include<iostream>
using namespace std;
class A
{
int a;
public:
A() {cout << "consA" << endl; }
~A() { cout << "desA" << endl; }
//void showa() { cout << a << endl; }
};
class A2
{
public:
A2() { cout << "consA2" << endl;}
~A2() { cout << "desA2" << endl; }
};
class B
{
A a;
A2 b;
public:
B():b(),a()//调用顺序与函数编写顺序无关
{
cout << "consB" << endl;
}
~B() { cout << "desB" << endl; }
//void showb() { cout << b << endl; }
};
int main()
{
B a;
}
- 综上:
- 总的构造函数调用顺序:
- 先继承,再组合,都看声明时侯的顺序。
- 析构函数:
- 先组合,再继承,与声明的顺序反着来
练习:
(1)组合和继承混用时
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "A"; }
~A() { cout << "~A"; }
};
class B : public A
{
A p;
public:
B() { cout << "B"; }
~B() { cout << "~B"; }
};
int main()
{
B obj;
return 0;
}
先基类再组合
#include <iostream>
using namespace std;
class C
{
public:
C() { cout << "C"; }
~C() { cout << "~C"; }
};
class A
{
int n;
public:
A() { cout << "A"; }
~A() { cout << "~A"; }
};
class B : public A
{
C p;
public:
B() { cout << "B"; }
~B() { cout << "~B"; }
};
int main()
{
B obj;
return 0;
}
(2)组合(指针形式),继承,指定的析构函数顺序
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "A"; }
~A() { cout << "~A"; }
};
class B : public A
{
A* p;
public:
B() { cout << "B"; p = new A(); }
~B() { cout << "~B"; delete p; }
};
int main()
{
B obj;
return 0;
}
(3)派生类作为形参时
#include <iostream>
using namespace std;
class Base {
public:
Base() { cout << "Base" << endl; }
Base(Base& b) { cout << "copy base" << endl; }
~Base() { cout << "base destroyed" << endl; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived" << endl; }
Derived(Derived& d) {
cout << "copy Derived" << endl;
}
~Derived() {
cout << "Derived destroyed" << endl;
}
};
void fun(Derived d)//注意,传递的时候,形参也需要构造函数哟
{
cout << "fun called" << endl;
}
void main()
{
{
Derived d;
fun(d);
}
}
(4)基类指针开辟派生类的空间
#include <iostream>
using namespace std;
class A {
int a;
public:
A() { cout << "A default cons\n"; }
A(int n) { cout << "A cons " << n << "\n"; }
virtual ~A() { cout << "A des\n"; }
};
class B : public A {
public:
B() { cout << "B default cons\n"; }
B(int n) :A(n) { cout << "B cons " << n << "\n"; }
virtual ~B() { cout << "B des\n"; }
};
int main()
{
A* p = new B(10);
delete p;
return 0;
}
空间是什么类型的,相应的构造析构函数都需要使用
拷贝构造函数
#include<iostream>
using namespace std;
class A
{
int a;
public:
A(int i=0):a(i){}
A(const A& m) { a = m.a; }
void showa() { cout<<a; }
~A() { cout << "desA" << endl; }
//void showa() { cout << a << endl; }
};
class B:public A
{
int b;
public:
B(int i, int j) :A(i), b(j) {}
B(const B& m):A(m),b(m.b) {//拷贝构造函数
}
void showb() { A::showa();cout<<b; }
~B() { cout << "desB" << endl; }
//void showb() { cout << b << endl; }
};
int main()
{
B a(1, 2);
B b(a);
b.showb();
}
同名隐藏规则
#include <iostream>
using namespace std;
//---------------------------------
class Counter {
int value;
public:
Counter();
Counter(int value);
~Counter();
};//---------------------------------
class Example {
int value;
public:
Counter car;//表明Counter作为基类
Example();
Example(int val);
void disp();
~Example();
};//---------------------------------
Counter::Counter() {
cout << "Counter Constructor1" << endl;
value = 0;
}//---------------------------------
Counter::Counter(int val) {
cout << "Counter Constructor2" << endl;
value = val;
}//---------------------------------
Counter::~Counter() {
cout << "Counter Destructor" << endl;
}//---------------------------------
Example::Example() {
cout << "Example Constructor1" << endl;
value = 0;
}//---------------------------------
Example::Example(int val) {
cout << "Example Constructor2" << endl;
value = val;
}//---------------------------------
void Example::disp() {
cout << "value=" << value << endl;
}//---------------------------------
Example::~Example() {
cout << "Example Destructor" << endl;
}//---------------------------------
int main() {
Example e(2);
e.disp();
}
#include <iostream>
using namespace std;
class B1
{ public:
int nV;
void fun() {cout<<"Member of B1"<<endl;}
void fun(int n) {cout<<"Member of B1"<<endl;}
};
class D1: public B1
{ public:
int nV; //同名数据成员,派生类定义一个和基类同名成员,叫做覆盖
void fun(){cout<<"Member of D1"<<endl;}
}; //同名函数成员void main()
{ D1 d1;
//对象名.成员名标识, 访问D1类成员(若未强行指名,则通过派生类对象使用的是派生类中的同名成员))
d1.nV=1;
d1.fun();
//作用域限定符标识, 访问基类B1成员
d1.B1::nV=2;
d1.B1::fun();
d1.fun(10);
//基类同名函数全被隐藏,要访问基类同名成员就需要像上面一样,用基类名限定
}
#include <iostream>
using namespace std;
class B1
{
public:
int nV;
void fun() { cout << "Member of B1 with 1" << endl; }
void fun(int n) { cout << "Member of B1 with 2" << endl; }
};
class D1 : public B1
{
public:
int nV; //同名数据成员
void fun() { cout << "Member of D1" << endl; }
}; //同名函数成员void main()
int main()
{
D1 d1;
//对象名.成员名标识, 访问D1类成员
d1.nV = 1;
d1.fun();
//作用域限定符标识, 访问基类B1成员
d1.B1::nV = 2;
d1.B1::fun();
d1.fun(10);//不可行
d1.B1::fun(10);//可行
//基类同名函数全被隐藏
}
二义性
第一种:派生类继承两个基类
#include <iostream>
using namespace std;
class A
{
public:
void f();
};
class B
{
public:
void f();
void g();
};
class C:public A,public B
{
public:
};
int main()
{
C c;
c.f();
c.f(1);//二义性:第一种情况
}
第二种:派生类继承的几个基类由一个基类继承而来
class A
{
public:
void f();
};
class B:public A
{
public:
void g();
};
class C:public A
{
public:
};
class D:public C,public B
{
public:
};
相当于D的对象有两个f(),在通过D类访问时出现二义性
解决办法:
1)同名隐藏:声明同名函数,定义中写清楚调用哪个
2)写清类名进行约束
3)虚基类
虚基类
主要用来解决多继承时可能发生的对同一基类继承多
次而产生的二义性问题. – 为最远的派生类提供唯一的基类成员,而不重复产生
多次拷贝
#include <iostream>
using namespace std;
class B0 //声明基类B0
{
public:
int nV;
void fun() { cout << "Member of B0" << endl; }
};
class B1 : virtual public B0
{
public:
int nV1;
};
class B2 : virtual public B0
{
public:
int nV2;
};
class D1 : public B1, public B2 //派生类D1声明
{
public:
int nVd;
void fund() { cout << "Member of D1" << endl; }
};
void main()
{
D1 d1; //声明D1类对象d1
d1.nV = 2; //使用最远基类成员
d1.fun();
}
含有虚基类的构造函数
#include <iostream>
using namespace std;
class B0
{ public:
B0(int n){ nV=n; cout << "cons";}
int nV;
void fun(){cout<<"Member of B0"<<endl;}
};
class B1: virtual public B0
{ public:
B1(int a) : B0(a) {}
int nV1;
};
class B2: virtual public B0
{ public:
B2(int a) : B0(a) {}
int nV2;
};class D1: public B1, public B2
{
public:
D1(int a) : B0(a), B1(a), B2(a){}
int nVd;
void fund(){cout<<"Member of D1"<<endl;}
};
void main()
{
D1 d1(1);
d1.nV=2;
d1.fun();
}
**只有最远派生类对于最远基类的构造函数有效。**B0的nv的初始化只初始化了一次,其余对于nv的初始化被忽略。所以最远派生类一定对于所有基类在构造函数中都要构造,否则使用基类的构造函数,且在中间件派生类中对于最远基类的构造被忽略。
在建立对象时,只有最远派生类的构造函数调用虚基类的构造函数,该派生类的其它基类对虚基类构造函数的调用被忽略。