C++中与C#面向对象编程中的if(Boy b is Student)相对应的语法是什么?

3

我正在尝试使用C++实现上面的系统。以前,我使用C#和OOP来编写程序,所以这将是我第一次使用C++,我知道这两种语言之间存在一些差异。 我想做的是在Logbook类的成员列表中计算选民人数。

在C#中,我会使用

foreach(Member m in _members) {
    if(Member m is Voter) {
        votercount++;
    }
}

然而,我不确定在 C++ 中,这个实现是否正确? 在我的 Logbook.h 文件中。

class Logbook
{
private:
    std::list<Member> _members;

在我的 Logbook.cpp 文件中:

int Logbook::CandidateCount() {
  int membercount;
  for(Member m: _members) {
    if (Member* m=dynamic_cast<const Member*>(&Candidate)) membercount++;
  }
  return membercount;
}

它在&Candidate处显示错误,指出标识符Candidate未定义。这是因为Logbook类无法访问Candidate类吗?

非常感谢任何回复和帮助。


@Galik 你好,_members用于表示列表,因此代码如下:class Logbook { private: std::list<Member> _members; - hope estheim
如果候选人不被视为成员,那么为什么他们被称为成员?这看起来像是在设计缺陷周围工作,而不是修复设计。 - molbdnilo
在这种情况下,std::list<Members> 只能包含 Members。您不能将任何子类型放入其中。如果尝试这样做,它只会复制那些是Member的子类型部分,其余部分将被“切片”。 - Galik
如果你想在 C++ 中使用 多态性,你必须使用 指针 或者 引用。最好使用像 std::list<std::unique_ptr<Member>> 这样的 智能指针 - Galik
还有,您能把“_members”的定义放到问题中吗? - Galik
显示剩余6条评论
3个回答

1
这里有几个你做错的事情。首先,你没有初始化计数变量,所以它将从某个随机值开始使用(可能为零或其他值)。
接下来,你需要存储列表成员的指针,因为在C++中,多态只能通过指针实现。如果列表负责删除其元素(通常情况),则应使用智能指针,如std::unique_ptr
class Logbook {
public:
    int CandidateCount();

    // virtual destructor is (usually) important for polymorphic types
    virtual ~Logbook() = default;

    // store pointers in your list    
    std::list<std::unique_ptr<class Member>> members;
};

然后,您可以遍历该列表,尝试将每个指针动态转换为要计数的类型。如果返回有效的指针,则知道它是该类型。否则将返回nullptr
class Member: public Logbook {};
class Candidate: public Member {};
class Voter: public Member {};

int Logbook::CandidateCount()
{
    int membercount = 0; // initialize this!!!!

    for(auto& m : members) { // use reference here to avoid making a copy

        if(dynamic_cast<Candidate*>(m.get()))
            membercount++;
    }

    return membercount;
}

注意:如果您想做的不仅仅是计算候选人,您可以像这样保留从dynamic cast获得的指针:
class Candidate: public Member { public: void do_something(){} };

int Logbook::CandidateCount()
{
    int membercount = 0; // initialize this!!!!

    for(auto& m : members) { // use reference here to avoid making a copy

        if(auto c = dynamic_cast<Candidate*>(m.get())) {
            membercount++;

            // c is not nullptr and is type Candidate*    
            c->do_something(); // use your Candidate like this
        }
    }

    return membercount;
}

@Malik 首先感谢您的解释,非常易于理解。这是否意味着如果我想访问Candidate类的方法,我必须像上面那样迭代? 还有几个新关键字显示出来,auto关键字是用于编译器自动检测数据类型吗? 为什么在这里使用m.get()? - hope estheim
@hopeestheim 是的。但是如果你想要实际使用它们,你需要保留指针。所以使用 if(auto c = dynamic_cast<Candidate*>(m.get())) {/* 在这里使用 c */} - Galik
在这种情况下,c 的类型将是 Candidate* - Galik
谢谢Galik!你非常有帮助。 - hope estheim
@hopeestheim 是的,auto是一个快捷方式,可以告诉编译器检测类型。您只能在编译器具有检测类型所需的所有信息的情况下使用它。此外,m.get()被使用,因为m是类型为std::unique_ptr<Member>智能指针。获取它管理的指针的方法是调用其get()方法。 - Galik

0
int Logbook::CandidateCount()
{
    int membercount{};
    for(auto const &m : _members) {
       if (dynamic_cast<Member*>(m))
           ++membercount;
    }
    return membercount;
}

请问代码的哪个部分检查类型是否为Candidate类? - hope estheim
1
if (dynamic_cast<Member*>(m)) tries to cast to Member*. If you want to try to cast to Candidate*: if (dynamic_cast<Candidate*>(m)) - user10543939

0

我会尽量避免在C++中使用RTTI和dynamic_cast,而是想办法利用语言的面向对象特性来实现你要做的事情。

你已经有了两个继承自Member的类,所以你可以添加一个CountMember(int& voterCount)方法,让每个成员都记录自己的投票。然后你只需要为每个成员调用该方法。像这样:

class Member {
public:
  virtual void countMember(int& voterCount) = 0;
};

class Candidate : public Member {
public:
  void countMember(int&) override {}
};

class Voter : public Member {
public:
  void countMember(int& voterCount) override {
    voterCount++;
  }
};

class Logbook {
private:
  std::list<Member> _members;
public:
  int CandidateCount() {
    int votercount = 0;
    for(auto& member : _members) {
      member.countMember(votercount);
    }
    return votercount;
  }
};

这样做可以很容易地在添加新类的情况下添加自定义行为。

嗨,是的,这也可以工作,与我之前学习的C#面向对象继承方法非常相似! - hope estheim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接