C++ 中【子类转父类】和【父类转子类】的方法

本文通过一个C++示例程序展示了如何使用动态类型转换(dynamic_cast)在基类指针指向派生类对象时进行安全的向下转型,并调用派生类特有的成员函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

// EX_EXAM.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class A
{
public:
	virtual void foo() { cout<<"A foo"<<endl; }
	void pp() { cout<<"A pp"<<endl; }
};

class B: public A
{
public:
	void foo() { cout<<"B foo"<<endl; }
	void pp() { cout<<"B pp"<<endl; }
	void FunctionB() { cout<<"Excute FunctionB!"<<endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
	A a;
	B b;
	A *pa=&b;
	pa->foo();
	pa->pp();
	if (B *pb=dynamic_cast<B*>(pa))
	{
		cout<<"Convert from B to A successfully"<<endl;
		(dynamic_cast<B*>(pa))->FunctionB();
		(dynamic_cast<B*>(pa))->foo();
		(dynamic_cast<B*>(pa))->pp();
		(*pa).foo();
	}
	else
		cout<<"Cannot convert from B to A"<<endl;

	return 0;
}

<think>我们正在讨论C#中的转换问题,特别是将父类引用转换子类引用以访问子类特有的属性或方法。在C#中,这被称为向下型(downcasting)。根据引用[1]引用[2]的内容,我们知道: - 引用[1]提到:通过父类的引用变量无法调用子类特有的方法,但可以通过向下型(父类子类转换)来实现。 - 引用[2]提到:父类指针用子类实例化时,正常情况下只能访问子类中继承自父类的成员(包括重写的成员),不能访问子类特有的成员。 在C#中,向下型可以通过显式转换(explicit cast)或使用`as`运算符来实现。但是,这种转换只有在父类引用实际指向一个子类对象时才安全,否则会抛出异常(对于显式转换)或返回null(对于`as`运算符)。 以下步骤说明如何在C#中实现: ### 1. **定义父类子类** 首先,我们定义一个父类(例如`Crop`)一个子类(例如`AppleTree`),子类拥有一个父类没有的属性(例如`Variety`)。 ```csharp public class Crop { public string Name { get; set; } public virtual void Print() { Console.WriteLine($"I am a crop named {Name}"); } } public class AppleTree : Crop { public string Variety { get; set; } // 子类特有的属性 public AppleTree(string variety) { Name = "Apple Tree"; Variety = variety; } public override void Print() { Console.WriteLine($"I am an apple tree of variety: {Variety}"); } } ``` ### 2. **向下型访问子类属性** 如果有一个父类型的引用,但它实际指向一个子类对象,我们可以通过显式转换将其转换子类型,然后访问子类特有的属性。 ```csharp class Program { static void Main() { // 创建一个AppleTree对象,但用父类Crop的引用指向它 Crop crop = new AppleTree("Fuji"); // 调用Print方法(多态,会调用子类的实现) crop.Print(); // 输出: I am an apple tree of variety: Fuji // 尝试访问子类特有属性Variety(编译错误,因为crop是Crop型) // Console.WriteLine(crop.Variety); // 错误 // 向下型:将父类引用转换子类引用 // 方法1:显式转换(如果转换失败会抛出InvalidCastException) AppleTree appleTree = (AppleTree)crop; Console.WriteLine(appleTree.Variety); // 输出: Fuji // 方法2:使用as运算符(安全转换,失败返回null) AppleTree appleTree2 = crop as AppleTree; if (appleTree2 != null) { Console.WriteLine(appleTree2.Variety); // 输出: Fuji } } } ``` ### 3. **转换失败的情况** 如果父类引用指向的对象不是目标子类型,则转换会失败: - 显式转换会抛出`InvalidCastException`。 - `as`运算符会返回`null`。 ```csharp Crop anotherCrop = new Crop(); // 这是一个纯父类对象,不是子类对象 // 显式转换会抛出异常 // AppleTree tree = (AppleTree)anotherCrop; // 运行时错误 // 使用as运算符安全转换 AppleTree tree2 = anotherCrop as AppleTree; if (tree2 == null) { Console.WriteLine("Conversion failed."); // 会执行 } ``` ### 4. **使用`is`运算符进行型检查** 在转换前,可以使用`is`运算符检查引用是否指向目标型。 ```csharp if (crop is AppleTree) { AppleTree appleTree3 = (AppleTree)crop; Console.WriteLine(appleTree3.Variety); } ``` ### 总结 - **向下型**:在C#中,将父类引用转换子类引用,以便访问子类特有的成员。 - **安全方法**:使用`as`运算符(转换失败返回null)或结合`is`运算符进行显式转换(先检查再转换)。 - **注意事项**:确保父类引用实际指向目标子类对象,否则会引发异常或得到null。 通过这种方式,我们可以在C#中实现C++的向下型,并安全地访问子类属性[^1][^2]。 ### 相关问题 1. C#中向上向下型的区别是什么? 2. 如何在C#中安全地进行转换以避免运行时异常? 3. C#的`as`运算符显式转换有什么区别? 4. 多态在C#中如何实现?它与转换有什么关系? 5. 在C#中,如何判断一个对象的实际型?</think>### C# 父类引用转换子类引用访问子类属性 在 C# 中,将父类引用转换子类引用以访问子类特有属性称为**向下型**(Downcasting)。这需要通过显式转换实现,但必须确保父类引用实际指向子类对象,否则会抛出 `InvalidCastException`。以下是详细步骤示例: --- #### 1. **基本概念** - **向上型**(安全):子类对象赋值给父类引用(自动转换) ```csharp Crop crop = new AppleTree("富士"); // 父类引用指向子类对象[^1] ``` - **向下型**(需显式转换):父类引用子类引用以访问子类特有成员 ```csharp AppleTree apple = (AppleTree)crop; // 显式转换子类 Console.WriteLine(apple.Variety); // 访问子类特有属性 ``` --- #### 2. **安全转换方法** ##### (1) 显式转换 + 异常处理 ```csharp class Crop { } // 父类 class AppleTree : Crop // 子类 { public string Variety { get; } // 子类特有属性 public AppleTree(string variety) => Variety = variety; } // 使用示例 Crop crop = new AppleTree("富士"); // 向上型 try { AppleTree apple = (AppleTree)crop; // 向下型 Console.WriteLine($"品种: {apple.Variety}"); // 访问子类属性 } catch (InvalidCastException) { Console.WriteLine("转换失败:对象不是AppleTree型"); } ``` ##### (2) 使用 `as` 运算符(推荐) ```csharp AppleTree apple = crop as AppleTree; // 安全转换(失败返回null) if (apple != null) { Console.WriteLine($"品种: {apple.Variety}"); } else { Console.WriteLine("转换失败"); } ``` ##### (3) 使用 `is` 运算符检查型 ```csharp if (crop is AppleTree apple) // 型检查 + 转换 { Console.WriteLine($"品种: {apple.Variety}"); } ``` --- #### 3. **关键注意事项** 1. **型安全**:只有父类引用**实际指向子类对象**时才能成功转换[^2] ```csharp Crop wrongCrop = new Crop(); // 纯父类对象 AppleTree apple = wrongCrop as AppleTree; // 返回null ``` 2. **设计原则**:尽量避免向下型,优先使用多态(虚方法/重写) 3. **性能**:`as` `is` 运算符比 `try-catch` 更高效 --- #### 4. 完整代码示例 ```csharp using System; class Crop // 父类 { public virtual void Print() => Console.WriteLine("我是农作物"); } class AppleTree : Crop // 子类 { public string Variety { get; } // 子类特有属性 public AppleTree(string variety) => Variety = variety; public override void Print() => Console.WriteLine($"苹果树品种: {Variety}"); } class Program { static void Main() { // 向上型(父类引用指向子类对象) Crop crop = new AppleTree("富士"); crop.Print(); // 调用子类重写方法 → 输出: "苹果树品种: 富士" // 向下型访问子类特有属性 if (crop is AppleTree apple) { Console.WriteLine($"访问子类属性: {apple.Variety}"); // 输出: "富士" } // 错误转换示例 Crop realCrop = new Crop(); var result = realCrop as AppleTree; // 返回null Console.WriteLine(result == null ? "安全转换失败" : "成功"); } } ``` 输出结果: ``` 苹果树品种: 富士 访问子类属性: 富士 安全转换失败 ``` --- ### 关键总结 | **操作** | **语法** | **失败行为** | 使用场景 | |----------------|-----------------------------|-----------------------|----------------------| | 显式转换 | `(SubClass)parentRef` | 抛出 `InvalidCastException` | 确定型时 | | `as` 运算符 | `parentRef as SubClass` | 返回 `null` | 安全转换 | | `is` 运算符 | `parentRef is SubClass var` | 返回 `false` | 型检查+安全转换 | > ⚠️ **重要**:向下型破坏了面向对象的封装性,应优先考虑通过**虚方法重写**或**接口**实现多态[^1][^2]。 --- ### 相关问题 1. C# 中向上向下型的主要区别是什么? 2. 如何避免 `InvalidCastException` 异常? 3. 在哪些场景下必须使用向下型? 4. C# 的 `as` `is` 运算符底层如何实现? 5. 多态设计与向下型如何权衡?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值