我无法理解virtual
C++中关键字的用途.我非常了解C和Java,但我是C++的新手
来自维基百科
在面向对象的编程中,虚函数或虚方法是一种函数或方法,其行为可以通过具有相同签名的函数在继承类中重写.
但是我可以在不使用virtual
关键字的情况下覆盖如下所示的方法
#include <iostream>
using namespace std;
class A {
public:
int a();
};
int A::a() {
return 1;
}
class B : A {
public:
int a();
};
int B::a() {
return 2;
}
int main() {
B b;
cout << b.a() << endl;
return 0;
}
//output: 2
Run Code Online (Sandbox Code Playgroud)
如下所示,函数A :: a被成功覆盖了B :: a而不需要 virtual
令我困惑的是这个关于虚拟析构函数的陈述,也来自维基百科
如下例所示,对于C++基类来说,拥有一个虚拟析构函数以确保始终调用最派生类的析构函数非常重要.
那么virtual
还告诉编译器调用父代的析构函数?这似乎与我原来的理解virtual
"使功能可以覆盖" 非常不同
我花了2个多小时发现这个内存泄漏:
class Parent{
...
}
class Child:public Parent{
std::string str;
...
}
int main(){
Parent *ptr=new Child();
delete ptr;
}
Run Code Online (Sandbox Code Playgroud)
我通过将字符串移动到父类来修复它.为什么会发生内存泄漏?孩子的成员不应该被删除吗?
我不明白一件事.例如,我声明A类和B类是A的子类:
class A {
public:
int a;
}
class B : public A {
public:
int b;
}
Run Code Online (Sandbox Code Playgroud)
显然,如果我创建A或B的实例,它们在内存中的大小可以由类型确定.
A instanceA; // size of this will probably be the size of int (property a)
B instanceB; // size of this will probably be twice the size of int (properties a and b)
Run Code Online (Sandbox Code Playgroud)
但是如果我创建动态实例然后在以后释放它会怎样?
A * instanceAPointer = new A();
A * instanceBPointer = new B();
Run Code Online (Sandbox Code Playgroud)
这些是不同类的实例,但程序会将它们视为A类的实例.在使用它们时这很好但是如何释放它们呢?为了释放分配的内存,程序必须知道要释放的内存大小,对吧?
所以,如果我写
delete instanceAPointer;
delete isntanceBPointer;
Run Code Online (Sandbox Code Playgroud)
程序如何知道,有多少内存,从每个指针指向的地址开始,它应该是空闲的?因为很明显,对象具有不同的大小,但程序认为它们是A类型.
谢谢
可能的重复:
何时使用虚拟析构函数?
如果一个类(具有虚函数)及其继承类的所有数据成员都是非指针类型(意味着它不能保存任何动态内存),是否需要将析构函数声明为虚函数?
例子
class base {
int x;
public:
virtual void fn(){}
};
class der: public base {
int y;
public:
void fn(){}
};
Run Code Online (Sandbox Code Playgroud)
这里我们需要一个虚拟析构函数吗?
我有base
继承base
该类的类和类:
class base
{
};
class derived : public base
{
std::string str;
};
Run Code Online (Sandbox Code Playgroud)
我需要derived
使用指向类的指针来管理base
类,但以下代码会导致内存泄漏:
base* ptr = new derived();
delete ptr;
Run Code Online (Sandbox Code Playgroud)
我有演员ptr
,还是有更好的选择?
我试图了解虚拟析构函数.以下是此页面的复制粘贴何时使用虚拟析构函数?
在这里,您会注意到我没有声明Base的析构函数是虚拟的.现在,我们来看看以下代码段:
Run Code Online (Sandbox Code Playgroud)Base *b = new Derived(); // use b delete b; // Here's the problem!
[...]如果要防止通过基类指针删除实例,可以使基类析构函数受保护且非虚拟; 通过这样做,编译器将不允许您在基类指针上调用delete.
我不明白为什么通过拥有受保护的非虚基类析构函数来防止删除.编译器是否认为我们试图delete
从基类对象调用?什么是protected
有,这样做?
在C++中我知道当一个打算从基类继承时,通常应该使用虚拟析构函数.但是,使用C#我不知道该怎么做.请考虑以下代码:
public abstract class Character
{
private string characterName;
public int health;
Character()
{
}
~Character(){
}
public virtual void SetCharacterName( string tempName )
{
characterName = tempName;
}
public virtual string GetCharacterName( )
{
return characterName;
}
}
Run Code Online (Sandbox Code Playgroud)
(注意:我听说Unity的Unity3Ds实现与标准略有不同.也许忽略一些小的格式错误,代码似乎功能......)
我的第一直觉是将~Character()析构函数设为虚拟,将其定义为:
virtual ~Character(){
}
Run Code Online (Sandbox Code Playgroud)
但这样做会导致IDE返回错误.
在C#中,对于希望继承的抽象类,使用虚拟析构函数是必要的还是被认为是标准的?还是有其他方法用C#制作虚拟析构函数?
我知道,只要你有一个多态基类,基类就应该定义一个虚析构函数.因此,当删除派生类对象的基类指针时,它将首先调用派生类的析构函数.如果我在这里错了,请纠正我.
另外,如果基类析构函数是非虚拟的,则删除指向派生对象的基类指针将是未定义的行为.如果我错了,请纠正我.
所以我的问题是:为什么确切地说,当基类析构函数是非虚拟的时,对象不会被正确销毁?
我假设这是因为虚拟函数具有某种表,无论何时调用虚函数,它都会被记忆和参考.并且编译器知道当一个对象应该被删除时,它应该首先调用派生的析构函数.
我的假设是正确的吗?
假设存在Bar
使用Foo
对象的对象。所有权是独占的,因此Bar
得到Foo
一个std::unique_ptr
在其构造。我想Bar
使用Google Test框架进行测试,因此我编写了以下代码:
using namespace testing;
class Foo
{
public:
virtual int F() = 0;
};
class Bar
{
public:
Bar(std::unique_ptr<Foo>&& foo) : m_foo(std::move(foo))
{
}
int B()
{
return m_foo->F();
}
private:
std::unique_ptr<Foo> m_foo;
};
class MockFoo : public Foo
{
public:
MOCK_METHOD0(F, int());
};
class BarTest : public Test
{
};
TEST_F(BarTest, Test1)
{
auto mock_foo = std::make_unique<MockFoo>();
ON_CALL(*mock_foo, F()).WillByDefault(Return(1));
Bar bar(std::move(mock_foo));
auto val = bar.B();
EXPECT_THAT(val, …
Run Code Online (Sandbox Code Playgroud) 我正在努力想象出问题出在哪里,但看起来很奇怪.
我以更容易理解的方式重写了我的问题.
当它到达删除行时,debbug会产生一个断点.
PS.有趣的是,如果我们采用int b1并将其移动到Base2就行了.
BASE1:
#pragma once
class Base1
{
public:
Base1();
~Base1();
int b1;
};
Run Code Online (Sandbox Code Playgroud)
Base2.h:
#pragma once
#include <iostream>
#include <vector>
class Derived;
class Base2
{
public:
Base2();
~Base2();
std::vector <std::vector <Base2*>> vec;
void doSomething(Derived& d);
};
Run Code Online (Sandbox Code Playgroud)
Base2.cpp:
#include "Base2.h"
#include "Derived.h"
Base2::Base2()
{
//the numbers 1 and 5 does not really metter
vec.resize(1);
vec[0].resize(5);
}
Base2::~Base2()
{
}
void Base2::doSomething(Derived& d)
{
vec[0][d.b1] = new Derived();
delete vec[0][d.b1];
}
Run Code Online (Sandbox Code Playgroud)
衍生:
#pragma once
#include "Base1.h"
#include "Base2.h" …
Run Code Online (Sandbox Code Playgroud) c++ ×9
inheritance ×3
memory-leaks ×2
polymorphism ×2
c# ×1
c++11 ×1
googlemock ×1
googletest ×1
object ×1
oop ×1
protected ×1
syntax ×1