GCC C++编译器通过函数属性提供一系列扩展,例如:
int square(int) __attribute__((const));
Run Code Online (Sandbox Code Playgroud)
特别是两个属性,const并pure允许您声明函数的评估没有副作用,并且仅依赖于其参数(const),或仅依赖于其参数和全局变量(pure).这允许公共子表达式消除,这可能导致这样的函数被调用的次数少于在代码中写入的次数.
我的问题是这是否可以安全,正确和合理地用于虚拟成员函数:
struct Foo
{
virtual int square(int) __attribute__((pure)); // does that make sense?
};
Run Code Online (Sandbox Code Playgroud)
这有什么明智的语义吗?它是否允许?还是只是被忽略了?我担心在GCC文档中找不到答案.
这个问题的原因是存在一系列编译器选项-Wsuggest-attribute,这些选项使GCC产生关于可以放置这些属性以改进代码的建议.然而,它似乎最终甚至为虚拟功能提出了这些建议,我想知道是否应该认真对待这些建议.
在C++ 11中,我们在某些情况下被指导为按值传递对象,在其他情况下通过const-reference传递对象.但是,本指南取决于方法的实现,而不仅仅取决于其接口和客户的预期用途.
当我编写一个接口时,我不知道它将如何实现.写方法签名有一个很好的经验法则吗?例如 - 在下面的代码片段中,我应该使用Bar1或Bar2?
class IFoo
{
public:
virtual void Bar1(std::string s) = 0;
virtual void Bar2(const std::string& s) = 0;
};
Run Code Online (Sandbox Code Playgroud)
如果您同意正确的签名取决于实施,您可以在此处停止阅读.这是一个例子,说明我相信的原因.
在以下示例中,我们应该按值传递字符串:
class Foo
{
std::string bar;
Foo(std::string byValue)
: bar(std::move(byValue))
{
}
};
Run Code Online (Sandbox Code Playgroud)
现在我们可以在所有情况下以有效的方式实例化Foo:
Foo foo1("Hello world"); // create once, move once
Foo foo2(s); // the programmer wants to copy s. One copy and one move
Foo foo3(std::move(t)); // the programmer does not need t anymore. No copy at all …Run Code Online (Sandbox Code Playgroud) 如何在下面的代码中实现一般情况下的抽象基类.代码从我正在处理的库中简化.因此,int和double的显式实现不是一个选项.
template <typename T>
struct Foo
{
virtual void send(T t) = 0;
};
template <typename...T>
struct Bar : Foo<T>...
{
void send(T t) override { // does not compile because
// abstract method not implemented
}
};
int main() {
// example usage
Bar<int, double> b;
b.send(1);
b.send(2.3);
}
Run Code Online (Sandbox Code Playgroud)
提前谢谢了.
编辑:添加虚拟到抽象方法.
让我们假设我们有一个抽象类NonEditableSuperBase,我们从中创建另一个抽象类MyBase.
第一类NonEditableSuperBase具有虚函数(非纯虚).但是,我想强制说,如果某人创建了一个派生的类MyBase,他/她必须为所提到的函数提供一个实现.
因此,我的想法是将函数定义为纯虚拟MyBase.
我的问题:鉴于它只是虚拟的,这是一个坏主意NonEditableSuperBase吗?
例:
//NonEditableSuperBase.h
class NonEditableSuperBase
{
...
public:
virtual int someMethod(); //It has an implementation, suppose {return 42;}
};
//MyBase.h
class MyBase: public NonEditableSuperBase
{
public:
explicit MyBase();
virtual ~MyBase() = default;
virtual int someMethod() = 0; //I make it pure virtual
};
//MyBase.cpp
MyBase::MyBase() : NonEditableSuperBase() { }
//Now someone creates a derived class from MyBase.
class SuperDerived : public MyBase
{
public: …Run Code Online (Sandbox Code Playgroud) 编辑:已解决
我现在正在开发一个多线程项目,我有一个基础工作者类,具有从中继承的不同工作类.在运行时,工作类成为线程,然后根据需要执行工作.
现在,我有一个我写过的Director,它应该维护一个指向所有worker的指针数组,以便它可以从中检索信息,以及稍后修改它们中的变量.
我通过创建指向基类指针的指针来完成此操作:
baseWorkerClass** workerPtrArray;
Run Code Online (Sandbox Code Playgroud)
然后在Director的构造函数中,我动态地为基础worker类分配一个指针数组:
workerPtrArray = new baseWorkerClass*[numWorkers];
Run Code Online (Sandbox Code Playgroud)
在每个工作线程的构造函数中,worker调用director中的一个函数,该函数用于将该worker的指针存储在数组中.
以下是导演存储指针的方式:
Director::manageWorker(baseWorkerClass* worker)
{
workerPtrArray[worker->getThreadID()] = worker;
}
Run Code Online (Sandbox Code Playgroud)
以下是工人变体的示例.每个worker都继承自基类worker类,而base worker类包含纯虚函数,这些函数应该存在于所有worker变量中,以及一些在所有worker之间共享的变量.
class workerVariant : protected baseWorkerClass
{
public:
workerVariant(int id)
: id(id)
{
Director::manageWorker(this);
}
~workerVariant()
{
}
int getThreadID()
{
return id;
}
int getSomeVariable()
{
return someVariable;
}
protected:
int id;
int someVariable
};
Run Code Online (Sandbox Code Playgroud)
然后baseWorkerClass看起来像这样:
class baseWorkerClass
{
public:
baseWorkerClass()
{
}
~baseWorkerClass()
{
}
virtual int getThreadID() = 0;
virtual int getSomeVariable() = 0;
};
Run Code Online (Sandbox Code Playgroud)
在每个worker变量完成初始化之后,我应该得到一个指向baseWorkerClass对象的指针数组.这意味着我应该能够,例如,使用其ID作为数组的索引来获取某个工作中给定变量的值,如下所示: …
编辑:花了一点时间理解我写的代码后,我仍然不知道它有什么问题.这是我从中派生我的类的基类:
///ContactResultCallback is used to report contact points
struct ContactResultCallback
{
short int m_collisionFilterGroup;
short int m_collisionFilterMask;
ContactResultCallback()
:m_collisionFilterGroup(btBroadphaseProxy::DefaultFilter),
m_collisionFilterMask(btBroadphaseProxy::AllFilter)
{
}
virtual ~ContactResultCallback()
{
}
virtual bool needsCollision(btBroadphaseProxy* proxy0) const
{
bool collides = (proxy0->m_collisionFilterGroup & m_collisionFilterMask) != 0;
collides = collides && (m_collisionFilterGroup & proxy0->m_collisionFilterMask);
return collides;
}
virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0,const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1) = 0;
};
Run Code Online (Sandbox Code Playgroud)
现在这是我的派生类:
class DisablePairCollision : public btCollisionWorld::ContactResultCallback
{
public:
virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* …Run Code Online (Sandbox Code Playgroud) 考虑以下示例
#include <iostream>
struct PureVirtual {
virtual void Function() = 0;
};
struct FunctionImpl {
virtual void Function() {
std::cout << "FunctionImpl::Function()" << std::endl;
}
};
struct NonPureVirtual : public FunctionImpl, public PureVirtual {
using FunctionImpl::Function;
};
int main() {
NonPureVirtual c;
c.Function();
}
Run Code Online (Sandbox Code Playgroud)
编译器(GCC 4.9,Clang 3.5)退出时出错
test.cpp:18:20: error: variable type 'NonPureVirtual' is an abstract class
NonPureVirtual c;
^
test.cpp:4:18: note: unimplemented pure virtual method 'Function' in 'NonPureVirtual'
virtual void Function() = 0;
^
Run Code Online (Sandbox Code Playgroud)
但是当我没有得到形式时,PureVirtual一切都还可以.这很奇怪,因为标准 10.4.4说
如果一个类包含或继承至少一个最终覆盖为纯虚拟的纯虚函数,则该类是抽象的. …
c++ polymorphism abstract-class virtual-functions pure-virtual
我知道需要纯虚拟析构函数的情况.我也知道,如果我们不为它们提供实现,它将给我一个链接器错误.我不明白为什么在代码片段中应该是这种情况,如下所示:
int main()
{
Base * p = new Derived;
}
Run Code Online (Sandbox Code Playgroud)
这里没有删除,所以没有调用析构函数,所以不需要它的实现(假设它应该像其他正常的函数一样声明但未定义,链接器只在我们调用它们时才会抱怨)...或者我遗漏了什么?
我需要理解为什么这应该是一个特例?
编辑:基于BoBTFish的评论
这是我的Base和Derived类
class Base
{
public:
Base(){}
virtual ~Base() = 0;
};
class Derived : public Base
{
};
Run Code Online (Sandbox Code Playgroud) 我确信我们都看到了由于导致调用纯虚函数的错误导致崩溃的代码.一个简单的例子是这样的:
struct Base
{
Base() { method(); }
virtual void method() = 0;
};
struct Derived : Base
{
void method() {};
};
int main()
{
Derived d;
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,该呼叫method()中的Base构造是专门引用为部分C++标准的10.4/6未定义的行为,所以这是毫不奇怪的是,我们最终崩溃.(g ++和Clang都警告过这一点,事实上,尽管Clang成功了,但是这个例子的链接失败了g ++.)
但是,只是为了好玩,任何人都能想出一种方法来调用一个不依赖于未定义行为的纯虚函数吗?
(我想你可以说,如果存在这样的方法,那么C++标准就有缺陷,但我只是好奇......)
编辑:几个答案的家伙,谢谢你,但我应该说清楚,我发现对一个纯虚函数进行非虚拟调用是合法的(提供一个定义存在于某处).我更想知道法律中是否有任何可能导致虚拟呼叫的聪明漏洞,因此很可能是在没有定义的常见情况下崩溃.
例如,也许通过多重继承,可以执行一些聪明的(合法的)强制转换,但最终会method()被称为"错误的"(未实现的)PV ,这种事情.我只是觉得这是一个有趣的脑力激荡器:-)
我试图在这个答案中 "修复"这个例子,以演示如何调用纯虚函数.
#include <iostream>
using namespace std;
class A
{
int id;
public:
A(int i): id(i) {}
int callFoo() { return foo(); }
virtual int foo() = 0;
};
class B: public A
{
public:
B(): A(callFoo()) {}
int foo() { return 3; }
};
int main() {
B b; // <-- this should call a pure virtual function
cout << b.callFoo() << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是我没有得到运行时错误(使用C++ 4.9.2),但输出3.我尝试使用Borland C++ 5.6.4,但是我遇到了访问冲突.我认为foo()在基类的构造函数的调用中应该是纯虚拟的.
谁错了?我应该尝试更多的编译器吗?我是否正确理解虚函数?