9.5学习记录

一,派生类

在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。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

需要重点理解
    

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值