一,派生类
在C++中,没有所谓的"总派生类"的概念。派生类是继一个已有的类派生出的新类。派生类可以继承基类的成员变量和成员函数,并且可以在派生类中添加新的成员变量和成员函数。
在C++中,一个类可以派生出多个派生类,每个派生类都可以继承基类的成员变量和成员函数,并且可以在派生类中添加新的成员变量和成员函数。这种多层次继承的关系可以用树形结构来表示,其中基类在最上面,每个派生类都有自己的分支。
总之,C++中的派生类是一个基于已有的类创建新类的方法,可以继承基类的成员变量和成员函数,并且可以在派生类中添加新的成员变量和成员函数。
可以理解为父类。
举个例子
// 引入iostream库,用于在控制台输出和接收输入
#include <iostream>
// 使用std命名空间,这是一种方便的方式,使得我们无需在程序中每次使用std库的函数或对象时都写出std::
using namespace std;
// 定义一个名为base的类,它有一个私有成员变量x,表示类的内部状态
class base {
int x; // 私有成员变量x
public:
// 这是一个公有成员函数,用于设置x的值
void setx(int n) { x = n; }
// 这是一个公有成员函数,用于获取x的值
int getx() { return x; }
// 这是一个公有成员函数,用于在控制台输出x的值
void showx() { cout << x << endl; }
};
// 定义一个名为derived的类,它从base类派生,并且有一个私有成员变量y
class derived : public base {
int y; // 私有成员变量y
public:
// 这是一个公有成员函数,用于设置y的值
void sety(int n) { y = n; }
// 这是一个公有成员函数,用于通过getx函数获取x的值并设置给y
void sety() { y = getx(); }
// 这是一个公有成员函数,用于在控制台输出y的值
void showy() { cout << y << endl; }
};
// 这是主函数,程序的执行从这里开始
int main() {
// 创建一个derived类的对象obj
derived obj;
// 使用setx函数设置obj的基类成员x的值为10
obj.setx(10);
// 使用sety函数设置obj的y的值为20
obj.sety(20);
// 使用showx函数在控制台输出obj的x的值,输出应为10
obj.showx();
// 使用showy函数在控制台输出obj的y的值,输出应为20
obj.showy();
// 使用sety函数(没有参数的版本)设置obj的y的值为obj的x的值(通过getx函数获取),所以现在y的值为10
obj.sety();
// 使用showx函数在控制台输出obj的x的值,输出应为10(因为之前已经通过sety函数设置为10)
obj.showx();
// 使用showy函数在控制台输出obj的y的值,输出应为10(因为之前已经通过sety函数设置为10)
obj.showy();
return 0; // 主函数返回0,表示程序成功执行完毕。这是C++的约定。
}
二.protect成员
基类中protected的成员
类内部:可以访问
类的使用者:不能访问
类的派生类成员:可以访问
#include <iostream>
class B {
private:
int i;
protected:
int j;
public:
int k;
};
class D : public B {
public:
void f() {
// cannot access 派生类不可访问基类私有成员
j = 2; //派生类可以访问基类保护成员
k = 3;
}
};
int main() {
D d;
d.j = 2; // cannot access 保护成员,类的使用者不能访问
d.k = 3;
return 0;
}
三.保护继承
派生方式为protected的继承称为保护继承,在这种继承方式下,
基类的public成员在派生类中会变成protected成员,
/*
派生方式为protected的继承称为保护继承,在这种继承方式下,
基类的public成员在派生类中会变成protected成员,
基类的protected和private成员在派生类中保持原来的访问权限
*/
#include <iostream>
using namespace std;
class Base {
int x;
protected:
int getx() { return x; }
public:
void setx(int n) { x = n; }
void showx() { cout << x << endl; }
};
class Derived : protected Base {
int y;
public:
void sety(int n) { y = n; }
void sety() { y = getx(); } //访问基类的保护成员
void showy() { cout << y << endl; }
};
int main() {
Derived obj;
obj.setx(10); //错误
obj.sety(20);
obj.showx(); //错误,
obj.showy();
}
/*
解释:
如最上面文字所示:保护继承会将基类的public变为protected,而对于protected成员,
外部去使用保护成员的时候,会报错,所以setx与showx访问错误,而对于派生类,则可直接访问基类的保护成员,
在派生类中,y=getx()可正常访问!
*/
基类的protected和private成员在派生类中保持原来的访问权限
我不能用我自己的保护成员,但我可以用我的父类的保护成员
四.私有继承
基类的中的public成员在派生类中是private, private成员在派生类中不可访问。
#include <iostream>
using namespace std;
class Base {
int x;
public:
void setx(int n) { x = n; }
int getx() { return x; }
void showx() { cout << x << endl; }
};
//私有继承
//基类的中的public成员在派生类中是private, private成员在派生类中不可访问。
class derived : private Base {
int y;
public:
void sety(int n) { y = n; }
void sety() { y = getx(); }
void showy() { cout << y << endl; }
};
int main() {
derived obj;
obj.setx(10); // cannot access
obj.sety(20);
obj.showx(); // cannot access
obj.showy();
}
在C++中,类的私有成员只能被类的成员函数或友元函数访问。类的使用者不能直接访问类的私有成员,需要通过类的公有成员函数或友元函数来实现对私有成员的访问。
五.基类与派生类
举个例子
#include <iostream>
using namespace std;
class A {
int a;
public:
void setA(int x) { a = x; }
int getA() { return a; }
};
class B : public A {
int b;
public:
void setB(int x) { b = x; }
int getB() { return b; }
};
void f1(A a, int x)
{ a.setA(x);
/*cout << a.getA() << endl;*/
}
void f2(A* pA, int x) { pA->setA(x); }
void f3(A& rA, int x) { rA.setA(x); }
int main() {
A a1, * pA;
B b1;
a1.setA(1);
b1.setA(2);
//把派生类对象赋值给基类对象。
a1 = b1;
cout << a1.getA() << endl;
cout << b1.getA() << endl;
a1.setA(10);
cout << a1.getA() << endl;
cout << b1.getA() << endl;
//把派生类对象的地址赋值给基类指针。
pA = &b1;
pA->setA(20);
cout << pA->getA() << endl;
cout << b1.getA() << endl;
//用派生类对象初始化基类对象的引用。
A& ra = b1;
ra.setA(30);
cout << pA->getA() << endl;
cout << b1.getA() << endl;
b1.setA(7);
cout << b1.getA() << endl;
f1(b1, 100);//生成的副本是100,但是在函数外,不改变它的值
cout << "1111111111" << endl;
cout << b1.getA() << endl; // 7
f2(&b1, 200);
cout << b1.getA() << endl;
f3(b1, 300);
cout << b1.getA() << endl;
return 0;
}
六.直接基类与间接基类
// 引入iostream库,提供输入/输出功能
#include <iostream>
// 使用std命名空间,这样我们就可以直接使用std中的名字,而不用每次都在前面加上std::
using namespace std;
// 定义一个名为A的类,该类有一个私有成员变量x
class A {
int x; // 私有成员变量x
public:
// A类的构造函数,接受一个int类型的参数aa,并将aa的值赋给x,然后打印出"Constructing A"的信息
A(int aa) {
x = aa;
cout << "Constructing A" << endl;
}
// A类的析构函数,它会在对象生命周期结束时被调用,例如当对象超出其作用域或被删除时,打印出"Destructing A"的信息
~A() { cout << "Destructing A" << endl; }
};
// 定义一个名为B的类,该类从A公有派生(继承)
class B : public A {
public:
// B类的构造函数,接受一个int类型的参数x,并调用A的构造函数来初始化其基类部分,然后打印出"Constructing B"的信息
B(int x) : A(x) { cout << "Constructing B" << endl; }
};
// 定义一个名为C的类,该类从B公有派生(继承)
class C : public B {
public:
// C类的构造函数,接受一个int类型的参数y,并调用B的构造函数来初始化其基类部分,然后打印出"Constructing C"的信息
C(int y) : B(y) { cout << "Constructing C" << endl; }
};
// 主函数开始执行程序
int main() {
// 创建一个C类的对象c,并给它传递一个初始值为1的参数
C c(1);
return 0; // 主函数返回0,表示程序成功执行完毕
}
这个程序定义了三个类:A、B和C。每个类都有一个构造函数和一个析构函数。构造函数在对象被创建时被调用,而析构函数在对象被销毁时被调用。在main函数中,创建了一个C类的对象c,因此会按照继承顺序依次调用A、B和C的构造函数。
七.菱形继承
#include <iostream>
using namespace std;
class A {
public:
void vf() { cout << "I come from class A" << endl; }
};
class B : public A {};
class C : public A {};
class D : public B, public C {};
int main() {
D d;
d.vf(); // error
return 0;
}
上述代码定义了四个类:A、B、C和D。
类A有一个公有成员函数vf(),该函数输出一条消息。
类B和类C都从类A公有派生,因此它们都继承了类A的公有成员。
类D同时从类B和类C公有派生,这使得类D成为类B和类C的派生类。然而,需要注意的是,在这种情况下,类D会同时继承来自类B和类C的类A的成员。因此,理论上,类D的对象应该能够调用vf()。
然而,在你提供的代码中,调用d.vf()会引发一个错误。这是因为当一个类从多个基类派生时,如果这些基类有相同的成员(在这种情况下是vf()),则不明确哪个基类的成员应该被访问。这个问题称为菱形继承问题(diamond inheritance problem)。
解决菱形继承问题的一种常见方法是使用虚继承(virtual inheritance)。通过虚继承,类D可以明确指定它继承自类A的vf()成员函数。
下面是修改后的代码:
#include <iostream>
using namespace std;
class A {
public:
virtual void vf() { cout << "I come from class A" << endl; }
};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {
public:
using A::vf; // 使用A的vf()成员函数
};
int main() {
D d;
d.vf(); // 输出"I come from class A"
return 0;
}
在这个修改后的代码中,类A的成员函数vf()被声明为虚函数(virtual)。类B和类C使用虚继承从类A公有派生,这样它们就共享同一个基类A的对象。类D从类B和类C公有派生,并通过使用关键字using
来显式地使用类A的vf()成员函数。
这样,当我们调用d.vf()时,就会明确地调用来自类A的vf()成员函数,并输出"I come from class A"。
八.虚基类
#include <iostream> // 引入iostream库,用于输入和输出
using namespace std; // 使用命名空间std,这样就可以直接使用std中的名字,而不用每次都在前面加上std::
class A { // 定义一个名为A的类
int a; // 定义一个名为a的成员变量
public: // 下面为public部分,定义了类的公共接口
A(int x) { // 定义A的构造函数,接收一个int类型的参数x
a = x; // 将参数x赋值给成员变量a
cout << "Virtual Bass A..." << endl; // 输出一条消息,表示正在创建A类的对象
}
};
class B : virtual public A { // 定义一个名为B的类,通过虚继承从A公有派生
public:
B(int i) : A(i) { // 定义B的构造函数,接收一个int类型的参数i,并调用A的构造函数进行初始化
cout << "Virtual Bass B..." << endl; // 输出一条消息,表示正在创建B类的对象
}
};
class C : virtual public A { // 定义一个名为C的类,通过虚继承从A公有派生
int x; // 定义一个名为x的成员变量
public:
C(int i) : A(i) { // 定义C的构造函数,接收一个int类型的参数i,并调用A的构造函数进行初始化
cout << "Constructing C..." << endl; // 输出一条消息,表示正在创建C类的对象
x = i; // 将参数i赋值给成员变量x
}
};
class ABC : public C, public B { // 定义一个名为ABC的类,通过公有继承从C和B公有派生
public:
// 下面的构造函数会初始化C和B中的A,但不会初始化ABC中的A,因为ABC没有定义自己的A
// 虚基类由最终派生类初始化
ABC(int i, int j, int k)
: C(i), B(j), A(i) // L1,这里必须对A进行初始化,所以重复使用了A的构造函数
{
cout << "Constructing ABC..." << endl; // 输出一条消息,表示正在创建ABC类的对象
}
};
int main() { // 主函数,程序从这里开始执行
ABC obj(1, 2, 3); // 创建一个ABC类的对象obj,并给它传递三个参数1、2和3
return 0; // 主函数返回0,表示程序成功执行完毕
}
基类先初始化,然后再是派生类初始化,所以是CAB
再看一个例子
#include <iostream> // 引入输入输出库,允许程序使用cin, cout等函数
using namespace std; // 使用std命名空间,这样我们就可以直接使用std中的名字,而不用每次都在前面加上std::
class A { // 定义一个名为A的类
int a; // 在类A中定义一个名为a的成员变量
public: // 下面为public部分,定义了类的公共接口
A() { cout << "Constructing A" << endl; } // 定义A的默认构造函数,该函数在创建A类对象时会被调用,并输出一条消息
}; // 类定义结束
class B { // 定义一个名为B的类
public: // 下面为public部分,定义了类的公共接口
B() { cout << "Constructing B" << endl; } // 定义B的默认构造函数,该函数在创建B类对象时会被调用,并输出一条消息
}; // 类定义结束
class B1 : virtual public B, virtual public A { // 定义一个名为B1的类,通过虚继承从B和A公有派生
public: // 下面为public部分,定义了类的公共接口
B1(int i) { cout << "Constructing B1" << endl; } // 定义B1的构造函数,接收一个int类型的参数i,并输出一条消息
}; // 类定义结束
class B2 : public A, virtual public B { // 定义一个名为B2的类,通过公有继承从A和B公有派生
public: // 下面为public部分,定义了类的公共接口
B2(int j) { cout << "Constructing B2" << endl; } // 定义B2的构造函数,接收一个int类型的参数j,并输出一条消息
}; // 类定义结束
class D : public B1, public B2 { // 定义一个名为D的类,通过公有继承从B1和B2公有派生
public: // 下面为public部分,定义了类的公共接口
D(int m, int n) : B1(m), B2(n) { cout << "Constructing D" << endl; } // 定义D的构造函数,接收两个int类型的参数m和n,并调用B1和B2的构造函数进行初始化,并输出一条消息
A a; // 在类D中定义一个名为a的A类对象
}; // 类定义结束
int main() { // 定义主函数,程序从这里开始执行
D d(1, 2); // 创建一个D类的对象d,并给它传递两个参数1和2
return 0; // 主函数返回0,表示程序成功执行完毕
} // 主函数结束
输出结果为
Constructing B
Constructing A
Constructing B1
Constructing A
Constructing B2
Constructing A
Constructing D
D:\CODE\9yuelianxi\x64\Debug\9yuelianxi.exe (进程 19788)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
需要重点理解