C++中“Virtual outside class declaration ”解决办法

本文通过定义Shape基类和Circle派生类演示了C++中虚析构函数的作用及正确使用方法,并展示了如何避免析构过程中的内存泄漏问题。

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

C++中“Virtual outside class declaration ”解决办法

(1)定义两个类:Shape类和Circle类,Circle类共有继承Shape类,借此演示虚析构函数的作用。

Shape类头文件

#ifndef SHAPE_H
#define SHAPE_H
#include <iostream>
using namespace std;
class Shape
{
    public:
      Shape();
      virtual ~Shape();
      virtual double calcArea();
    //private:
};
#endif

Shape类实现

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

Shape::Shape()
{
    cout<<"Shape()"<<endl;
}
Shape::~Shape()
{
    cout<<"~Shape()"<<endl;
}
double Shape::calcArea()
{
    cout<<"shape-calcArea()"<<endl;
    return 0;
}

Circle类头文件

#include "Shape.h"
class Circle:public Shape
{
    public:
        Circle(double r);
        ~Circle();
        double calcArea();
    protected:
        double m_dR;
};

Circle类实现

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

Circle::Circle(double r)
{
    cout<<"Circle()"<<endl;
    m_dR=r;
}
Circle::~Circle()
{
    cout<<"~Circle()"<<endl;
}
double Circle::calcArea()
{
     cout<<"Circle--Circle()"<<endl;
     return 3.14*m_dR*m_dR;
}

main函数

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

int main(void)
{
    //在堆上实例化一个Shape类型的对象指针,以子类的对象给父类的
    //对象指针初始化。
    Shape *p1=new Circle(5);
    //使用该指针访问calcArea()函数,若Shape类的calcArea函数不是虚函数,
    //则调用Shape类的该函数,若是虚函数,则调用子类的calcArea函数。
    p1->calcArea();
    delete p1;
    p1=NULL;
    system("pause");
    return 0;
}

运行结果:
上述代码的运行结果

(2)错误示例

   虚析构函数使用错误:

class Shape
{
    public:
      Shape();
      virtual ~Shape();
      virtual double calcArea();
    //private:
};
virtual Shape::~Shape()
{
    cout<<"~Shape()"<<endl;
}

解释:Take the “virtual”s out of the cpp file. You only need to specify it in the class definition (in the header file). After that, the compiler knows that it’s virtual.
也就是说你只需要在类的定义中将成员函数声明为 virtual类型即可,而不用在成员函数的实现中加入 virtual关键字。

(3)定义父类的指针指向子类的对象时,先调用父类的构造函数再调用子类的构造函数,释放内存时,若父类的析构函数不是虚析构函数,则只释放调用父类的析构函数而不调用子类的析构函数,从而造成内存泄漏。

<think>我们正在讨论C++中类的定义和使用。根据引用内容,我们可以总结如下:1.引用[1]提到C++中struct和class都可以用来定义类,区别在于默认访问权限:struct默认为public,class默认为private。2.引用[2]和[3]说明了类的实现可以在类定义内部完成,也可以在类定义外部完成。3.引用[4]展示了类的继承和多态的应用,通过基类指针调用不同子类的虚函数。4.引用[5]提到了类重定义的问题,以及如何通过头文件保护(#ifndef等)来避免重复定义。因此,我们将围绕这些点来组织回答。###回答结构:1.**类的定义**:解释如何用class关键字定义类,包括访问权限(public,private,protected)。2.**类的实现**:分内部实现和外部实现两种方式。3.**类的实例化**:如何创建对象并使用。4.**继承与多态**:简要介绍(根据引用[4]的例子)。5.**避免重定义**:头文件保护的使用。###注意:-使用中文回答。-内联数学表达式用$...$(但本问题中可能较少,若有计算相关的内容则使用)。-独立公式用$$...$$(同样,本问题可能较少)。-生成相关问题。下面开始回答。</think>### C++ 类的定义与使用方法 #### 一、类的定义(Declaration) 在 C++ 中,`class` 是定义类的关键字,其基本语法结构为: ```cpp class ClassName { public: // 公有成员(外部可访问) // 成员函数声明 void setValues(int a, int b); void printInfo(); private: // 私有成员(仅类内可访问) // 成员变量 int data1; double data2; }; ``` 关键特性: 1. **访问权限** - `public`:类外部可直接访问(默认权限为 `private`) - `private`:仅类内成员函数可访问(默认权限) - `protected`:类内及子类可访问 > ⚠️ 与 `struct` 的区别:`struct` 默认访问权限为 `public`,而 `class` 默认为 `private`[^1]。 2. **成员组成** - 成员变量(属性) - 成员函数(方法) --- #### 二、类的实现(Implementation) 成员函数可通过两种方式实现: 1. **类内直接实现** 在类定义中直接编写函数体(适合简单函数): ```cpp class Point { public: void setPoint(int x, int y) { xPos = x; // 直接赋值 yPos = y; } private: int xPos, yPos; }; ``` 2. **类外分离实现**(推荐) 声明与实现分离(需用 `ClassName::` 限定作用域): ```cpp // 头文件 Point.h class Point { public: void setPoint(int x, int y); // 声明 private: int xPos, yPos; }; // 源文件 Point.cpp #include "Point.h" void Point::setPoint(int x, int y) { // 类外实现 xPos = x; yPos = y; } ``` > ✅ 优势:提高代码可维护性,避免头文件膨胀[^2][^3]。 --- #### 三、类的实例化与使用 1. **创建对象** ```cpp Point p1; // 栈上创建 Point* p2 = new Point; // 堆上动态创建 ``` 2. **访问成员** ```cpp p1.setPoint(10, 20); // 调用公有方法 // p1.xPos = 5; // 错误!私有成员不可外部访问 ``` 3. **继承与多态**(扩展应用) ```cpp class Animal { // 基类 public: virtual void sound() = 0; // 纯虚函数(抽象类) }; class Dog : public Animal { // 派生类 public: void sound() override { cout << "Woof!"; } // 重写虚函数 }; Animal* animal = new Dog(); animal->sound(); // 输出 "Woof!"(多态)[^4] ``` --- #### 四、避免重定义错误 在头文件中使用预处理指令防止重复包含: ```cpp // Animal.h #ifndef ANIMAL_H // 头文件保护 #define ANIMAL_H class Animal { /* 类定义 */ }; #endif ``` > 💡 原理:编译器首次包含头文件时定义宏 `ANIMAL_H`,后续包含时因宏已定义跳过代码[^5]。 --- ### 关键总结表 | 特性 | 说明 | |---------------|----------------------------------------------------------------------| | **默认访问权限** | `class` 默认为 `private`,`struct` 默认为 `public`[^1] | | **成员实现** | 类内实现(简单场景) / 类外实现(推荐分离)[^2][^3] | | **多态** | 通过 `virtual` 函数和基类指针实现运行时绑定[^4] | | **头文件保护** | 使用 `#ifndef` + `#define` 避免重定义[^5] |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值