我对 C++ 比较陌生,一直在使用 OpenGL 开发基本的 3D 渲染引擎。我有以下问题:我有一个名为 GeomEntity 的类,它是所有几何基元的基类。我有另一个名为 DefaultMaterial 的类,它是所有材质的基类(由不同类型的着色器程序组成)。因为我将拥有多种类型的材质,例如:ColorMaterial、TextureMaterial、AnimatedMaterial 等,我需要在 GeomEntity 类中放置对材质的引用,以便从主应用程序我可以使用此功能设置任何材料:
void GeomEntity ::addMaterial (const DefaultMaterial *mat){
material=mat;////material is the member variable pointer of type DefaultMaterial
}
Run Code Online (Sandbox Code Playgroud)
但问题是,虽然所有这些材质都派生自 DefaultMaterial,但它们都有自己独特的方法,如果我默认将它们引用到 DefaultMaterial 变量,则无法触发这些方法。例如在主应用程序中:
Sphere sphere;
....
sphere.addMaterial(&animMaterial);///of type AnimatedMaterial
sphere.material->interpolateColor(timerSinceStart);
///doesn't happen anything as the sphere.material is
/// of type DefaultMaterial that has no interpolateColor() method
Run Code Online (Sandbox Code Playgroud)
我知道我可以使用模板或强制转换,但我想听听 C++ 中这种多态性的最佳实践。在 Java 或 C# 中,我真的会使用这样的东西:
((AnimatedMaterial)sphere.material).interpolateColor(timerSinceStart);
Run Code Online (Sandbox Code Playgroud) java中方法重载和重写的区别?没有给出正确答案。下面是java代码。
家长班
public class Parent {
void display() {
// some code
}
}
Run Code Online (Sandbox Code Playgroud)
儿童班
public class child extends Parent
void display(int a) {
// some code
}
}
Run Code Online (Sandbox Code Playgroud)
问题:这个方法是重载、重写还是无?
所以我知道在 C++ 中,虚拟方法适用于存储在表中的每个类,并且每个实例都有一个指向该表的指针。所以我的问题是子类表是什么样的。我将提供一个汇编指令:
vtable for Derived:
.long 0
.long _typeinfo for Derived
.long _Derived::set()
.globl _vtable for Base
.section .rdata$vtable for Base,"dr"
.linkonce same_size
.align 4
Run Code Online (Sandbox Code Playgroud)
因此,从这里我可以看到 Derived 有一个虚拟方法set(),但令我烦恼的部分是 Base 的 vtable。Derived vptr 是否保存指向 Base vptr 的指针,还是存储在 Derived 的 vtable 内。我注意到,在代码中,编译器仅将 vtable 存储在对象的 0 地址处,一次存储在 Base 构造函数中,一次存储在 Derived 中。为什么vtable没有被覆盖?
PS我不太明白指令
编辑:
class Base{
virtual void print() {
printf("Base");
}
}
class Derived : Base{
virtual void print(){
printf("Derived");
}
}
Run Code Online (Sandbox Code Playgroud) 我正在使用 Real World Haskell,并且作为练习的一部分,我意识到我想要一个类似于 的函数show :: Show(a) => a -> String,但它不影响字符串(而不是转义引号)。在伪代码中,我想要的是:
show' :: String -> String
show' = id
show':: Show(a) => a -> String
show' = show
Run Code Online (Sandbox Code Playgroud)
显然这不起作用(ghc 抱怨多个类型签名和多个声明)。看来我应该改用类型类。当我尝试编写此代码时,编译器不断建议我添加更多语言扩展:
{-# LANGUAGE FlexibleInstances, UndecidableInstances, IncoherentInstances #-}
class Show' a where
show' :: a -> String
instance Show' String where
show' = id
instance (Show a) => Show' a where
show' = show
-- x == "abc"
x = show' "abc"
y = show' 9
Run Code Online (Sandbox Code Playgroud)
起初,这似乎工作:x == "abc" …
有没有办法使用多态来为集合和多集提供一个泛型?如下。
注意:但仅适用于集合,(set, multiset)
template<typename T>
void foo(parent_set<T> &s) {
// do something
}
// main
set<int> s1 = {1, 2, 3, 4, 5};
foo(s1);
multiset<int> s2 = {1, 2, 2, 2, 2, 3};
foo(s2);
Run Code Online (Sandbox Code Playgroud) 有一些代码能够将实现operator()为std::function. 然后我尝试做同样的事情,但使用shared_ptr:
#include <functional>
#include <memory>
class WhiteNoise {
public:
WhiteNoise() {}
float operator() () {
return 0;
}
};
int main() {
//fails
std::shared_ptr<std::function<float()>> = std::dynamic_pointer_cast<std::function<float()>>(std::make_shared<WhiteNoise>());
//fails
std::shared_ptr<std::function<float()>> = std::make_shared<WhiteNoise>();
//works
std::function<float()> f = WhiteNoise();
}
Run Code Online (Sandbox Code Playgroud)
为什么我可以WhiteNoise当作std::function<float()>但不能shared_ptr<WhiteNoise>当作shared_ptr<std::function<float()>>
我是模板编程的新手,并且偶然发现了这个习惯用法。例如,类似:
class FooBase {
public:
virtual void do_something() = 0;
};
template <class Foo> // Foo is derived from FooBase
void g(Foo& foo) {
static_assert(std::is_base_of<FooBase, Foo>::value);
// ...
foo.do_something();
// ...
}
Run Code Online (Sandbox Code Playgroud)
在我看来,这种模式很有用,因为:
requires子句(如果可用的话)相比,指定属性、参数和返回类型很容易。FooBase标头即可清楚定义新 Foo 类的要求。FooBase类中。但是我担心使用的性能影响virtual函数对性能的影响。我的理解是运行时没有成本,因为该函数是从派生类调用的;但链接器将无法内联该函数。
另一个缺点是不允许虚拟模板功能。
最后,我担心这可能是一种代码味道,因为它使用运行时多态性功能来执行静态检查。C++20 概念可能是执行此操作的“正确”方法,但由于上述三个原因,它们似乎不太方便。
我与这个问题有类似的情况,我有两个共享公共接口函数的类,因此我将该函数(示例中的manage_data_in_memory())拉出到这两个类继承的基类中。然而,这两个类在其他方面并不相关,其中一个是多态的,而另一个则不是。也不希望有人声明这个基类的对象,因为它的存在只是为了防止代码重复(有办法强制执行吗?我知道纯虚函数可以防止对象被实例化,但是声明一个虚拟函数在派生类中不执行任何操作的纯虚拟似乎是糟糕的设计)。
在这种情况下,基类析构函数是否需要是虚拟的?这将迫使其他派生类也有一个它不需要的虚拟析构函数。谢谢。
所以现在我的代码格式为
class base { // don't instantiate an object of this, is there a way to enforce this?
public:
void manage_data_in_memory();
protected:
data;
};
class derived : public base
{
public:
void sendData();
private:
virtual void sendDataHelper(); // override this in derived classes which need to send custom data in addition to the default data
};
class derived2 : public base
{
public:
void do_Z_On_data()
};
Run Code Online (Sandbox Code Playgroud) 我试图在一个std::array(或任何其他容器)中存储从基类派生的许多对象,而无需对象切片。
下面的代码片段所需的输出是:
Hi from child1!
Hi from child2!
Run Code Online (Sandbox Code Playgroud)
和代码:
Hi from child1!
Hi from child2!
Run Code Online (Sandbox Code Playgroud)
我实际从代码中得到的输出是:
This shouldn't print
This shouldn't print
Run Code Online (Sandbox Code Playgroud)
显然派生对象已被切片。有什么办法可以避免这种情况吗?
预先感谢您提供的任何建议。
我\xe2\x80\x99m 尝试执行基本的多态性程序,但遇到了一些问题。\n我有 2 个文件,Currency.cpp 和 Dollar.cpp
\n货币.cpp 看起来像这样
\n#include <iostream>\nusing namespace std;\nclass Currency{\n/* code */\n};\nRun Code Online (Sandbox Code Playgroud)\nDollar.cpp 看起来像这样
\n#include <iostream>\nusing namespace std;\nclass Dollar : protected Currency{\n/* code /*\n};\nRun Code Online (Sandbox Code Playgroud)\n当我尝试在文件之间构建路径(在 mac 上使用命令 shift b)时,它告诉我货币不被识别为类(在 Dollar.cpp 中)。I\xe2\x80\x99m 不应该使用头文件,因为这是用于指定的分配。我应该做些什么?
\n我在 2020 m1 Mac 上使用 vs code,如果这与它有任何关系的话
\n编辑:\n感谢所有反馈。据我所知,我在这里问的是不可能的。这个作业是针对接受多种语言的数据结构和算法课,所以可能老师对c++之类的生疏了。我只需要使用头文件,教授就需要处理它,哈哈。
\npolymorphism ×10
c++ ×8
assembly ×1
c++-concepts ×1
containers ×1
haskell ×1
inheritance ×1
java ×1
multiset ×1
oop ×1
overloading ×1
overriding ×1
set ×1
std ×1
templates ×1
typeclass ×1
vtable ×1