te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>c++ - How can I make a std::vector<Derived_class> in a function whose input is an base class object? - Stack Overf
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - How can I make a std::vector<Derived_class> in a function whose input is an base class object? - Stack Overf

programmeradmin3浏览0评论

Say I have a base class Animal and its derived classes Dog and Cat, like this

class Animal{ public:
    virtual void makeSound(){
        std::cout << "I dont know what sound to make." << std::endl;
    }
};

class Dog: public Animal{ public:
    void makeSound() override{
        std::cout << "Woof!" << std::endl;
    }
};

class Cat: public Animal{ public:
    void makeSound() override{
        std::cout << "Meow!" << std::endl;
    }
};

The initial requirement is that a copy of a Dog or Cat object be made in a function whose input is an object of Animal. If the user gives a Dog object to the function, inside it another Dog object should be created. Something like

void MakeSound(Animal& animal){
    Animal another_animal = animal;
    another_animal.makeSound();
}

int main(){
    Dog dog;
    MakeSound(dog);
    // I want the output to be "Woof!",
    // but this will only give "I dont know what sound to make."
    // because of slicing.
}

Deepseek told me to use smart pointers.

class Animal {
public:
    virtual void makeSound(){
        std::cout << "I dont know what sound to make." << std::endl;
    }
    virtual ~Animal() = default; // Newly added line
    virtual std::unique_ptr<Animal> clone() const = 0; // Newly added line
};

class Dog : public Animal {
public:
    virtual void makeSound() override{
        std::cout << "Woof!" << std::endl;
    }
    std::unique_ptr<Animal> clone() const override { // Newly added line
        return std::make_unique<Dog>(*this); // Newly added line
    } // Newly added line
};

class Cat : public Animal {
public:
    virtual void makeSound() override{
        std::cout << "Meow!" << std::endl;
    }
    std::unique_ptr<Animal> clone() const override { // Newly added line
        return std::make_unique<Cat>(*this); // Newly added line
    } // Newly added line
};

void MakeSound(Animal& animal){
    auto another_animal = animal.clone(); // Newly added line
    another_animal.makeSound();
}

This seems to make sense, but I haven't tried it, because the requirement is changed suddenly. Now instead of making one copy, I have to make a std::vector of copies, whose size is determined in runtime. Something like

void AllMakeSound(Animal& animal, int num_animals){
    std::vector<Animal> animals(num_animals);
    for ( int i = 0; i < num_animals; i++ ){
        animals[i] = animal.clone();
    }
    for ( int i = 0; i < num_animals; i++ ){
        std::cout << "Animal " << i << " out of " << num_animals << std::endl;
        animals[i].makeSound();
    }
} // This will cause slicing too, so this function will not achieve the goal.

Now Deepseek cannot give much help without typeid comparison, something like

if ( animal.getType() == typeid(Dog) )

However, I cannot allow this, because this will need to enumerate all possible derived classes and compare, while I expect users to derive the Animal base class and create their own derived classes. Their derived classes cannot be anticipated. Perhaps someone would make a Godzilla class.

Therefore, I am here to seek help. The solution should:

  • Not need to enumerate all derived classes (This may excludes template and typeid comparison)
  • (Optionally) And produce std::vector that can be used as a normal one, including methods like push_back, empty, [], reserve.

Thanks for any help!

Say I have a base class Animal and its derived classes Dog and Cat, like this

class Animal{ public:
    virtual void makeSound(){
        std::cout << "I dont know what sound to make." << std::endl;
    }
};

class Dog: public Animal{ public:
    void makeSound() override{
        std::cout << "Woof!" << std::endl;
    }
};

class Cat: public Animal{ public:
    void makeSound() override{
        std::cout << "Meow!" << std::endl;
    }
};

The initial requirement is that a copy of a Dog or Cat object be made in a function whose input is an object of Animal. If the user gives a Dog object to the function, inside it another Dog object should be created. Something like

void MakeSound(Animal& animal){
    Animal another_animal = animal;
    another_animal.makeSound();
}

int main(){
    Dog dog;
    MakeSound(dog);
    // I want the output to be "Woof!",
    // but this will only give "I dont know what sound to make."
    // because of slicing.
}

Deepseek told me to use smart pointers.

class Animal {
public:
    virtual void makeSound(){
        std::cout << "I dont know what sound to make." << std::endl;
    }
    virtual ~Animal() = default; // Newly added line
    virtual std::unique_ptr<Animal> clone() const = 0; // Newly added line
};

class Dog : public Animal {
public:
    virtual void makeSound() override{
        std::cout << "Woof!" << std::endl;
    }
    std::unique_ptr<Animal> clone() const override { // Newly added line
        return std::make_unique<Dog>(*this); // Newly added line
    } // Newly added line
};

class Cat : public Animal {
public:
    virtual void makeSound() override{
        std::cout << "Meow!" << std::endl;
    }
    std::unique_ptr<Animal> clone() const override { // Newly added line
        return std::make_unique<Cat>(*this); // Newly added line
    } // Newly added line
};

void MakeSound(Animal& animal){
    auto another_animal = animal.clone(); // Newly added line
    another_animal.makeSound();
}

This seems to make sense, but I haven't tried it, because the requirement is changed suddenly. Now instead of making one copy, I have to make a std::vector of copies, whose size is determined in runtime. Something like

void AllMakeSound(Animal& animal, int num_animals){
    std::vector<Animal> animals(num_animals);
    for ( int i = 0; i < num_animals; i++ ){
        animals[i] = animal.clone();
    }
    for ( int i = 0; i < num_animals; i++ ){
        std::cout << "Animal " << i << " out of " << num_animals << std::endl;
        animals[i].makeSound();
    }
} // This will cause slicing too, so this function will not achieve the goal.

Now Deepseek cannot give much help without typeid comparison, something like

if ( animal.getType() == typeid(Dog) )

However, I cannot allow this, because this will need to enumerate all possible derived classes and compare, while I expect users to derive the Animal base class and create their own derived classes. Their derived classes cannot be anticipated. Perhaps someone would make a Godzilla class.

Therefore, I am here to seek help. The solution should:

  • Not need to enumerate all derived classes (This may excludes template and typeid comparison)
  • (Optionally) And produce std::vector that can be used as a normal one, including methods like push_back, empty, [], reserve.

Thanks for any help!

Share Improve this question edited Feb 17 at 18:48 张亦弛 asked Feb 17 at 18:37 张亦弛张亦弛 337 bronze badges 15
  • 4 -> std::vector<std::unique_ptr<Animal>> animals... – Jarod42 Commented Feb 17 at 18:42
  • 3 And then animals[i]->makeSound(); – Igor Tandetnik Commented Feb 17 at 18:43
  • 1 "The initial requirement is that a copy [..]" Strange requirement. – Jarod42 Commented Feb 17 at 18:44
  • 1 In the free function MakeSound() you go out of your way slice your object and remove the child (Dog in your specific example) portion when you assign animal to another_animal. It seems like you need to go back to your learning resources, which are hopefully not YouTube or a chatbot. – sweenish Commented Feb 17 at 18:44
  • 4 It should demonstrate how bad chatbots are if you can't vet the output. The prototype pattern (the clone() methods) has nothing to do with a simple virtual function override. – sweenish Commented Feb 17 at 18:48
 |  Show 10 more comments

2 Answers 2

Reset to default 1

As an optional way, replacing the virtual part of Animal to data. Then dogs and cats will be instance of Animal , making them easier to use.

In your example case, the simplest implementation would be as below. No smart_pointer, no inheritance (so no slicing).

class Animal
{
public:
    Animal( std::string Sound ) : m_Sound(Sound) {}
    void makeSound() const {    std::cout << m_Sound << std::endl;  }
private:
    std::string m_Sound;  //data
};

int main()
{
    Animal Dog( "Woof!" );
    Animal Cat( "Meow!" );
    //copy into vector
    std::vector<Animal> animals{    Dog, Dog, Cat   };

    //
    for( const auto &animal : animals ){    animal.makeSound(); }
    return 0;
}

Here, the "data" can be some other types(fuction pointer, functor, smart_pointer of some polymorphismic type, etc) if necessary. For example:

#include <functional>

class Animal
{
public:
    Animal( std::function<std::string(void)> SoundSource ) : m_SoundSource(SoundSource) {}
    void makeSound() const {    std::cout << m_SoundSource() << std::endl;  }
private:
    std::function<std::string(void)> m_SoundSource;  //data
};

//main
int main()
{
    Animal Dog( []()->std::string{  return "Woof!"; } );
    Animal Cat( []()->std::string{  return "Meow!"; } );
    //copy into vector
    std::vector<Animal> animals{    Dog, Dog, Cat   };

    //
    for( const auto &animal : animals ){    animal.makeSound(); }
    return 0;
}

The issue is in this function:

void MakeSound(Animal& animal){
    Animal another_animal = animal;
    another_animal.makeSound();
}

Specifically, the first line of the function where you create another_animal. By creating a concrete Animal object, you have sliced out the child class portion, which is why you get the base class behavior. There is only a base class object at that point.

Had you directly used animal, as in animal.makeSound();, the function behaves as you would expect. And while polymorphism through references is legal and has it's place and all that, it's much more common to use pointers to base objects instead.

For your request of a std::vector, pointers are the way to go. Because we want the vector to clean up on its own, a vector of smart pointers to Animals will be used.

If we didn't use smart pointers, we'd be on the hook to delete all the Animals manually, and that's bug-prone.

My local playground uses the fmt library for printing, but swapping in your own printing calls is trivial.

Finally, I made a subjective change where the public member function make_sound() is not virtual, but relies on a private virtual function to get the required information. It's not really needed for this example, but it's a habit and can provide more freedom in more complex projects. The principle is called NVI, or non-virtual interface.

#include <fmt/core.h>

#include <memory>
#include <string_view>
#include <vector>

class Animal {
 public:
  void make_sound() const { fmt::println("{}", get_sound()); }

 private:
  virtual std::string_view get_sound() const { return "..."; }
};

class Dog : public Animal {
  std::string_view get_sound() const override { return "Woof!"; }
};

class Cat : public Animal {
  std::string_view get_sound() const override { return "Meow."; }
};

void speak(Animal const& animal) { animal.make_sound(); }

void all_speak(std::vector<std::unique_ptr<Animal>> const& animals) {
  for (auto const& a : animals) {
    a->make_sound();
  }
}

int main() {
  Dog dog;
  speak(dog);

  std::vector<std::unique_ptr<Animal>> critters;
  critters.push_back(std::make_unique<Dog>());
  critters.push_back(std::make_unique<Cat>());
  all_speak(critters);
}

Output:

Woof!
Woof!
Meow.

The information you were given about clone() methods refers to the Protype pattern. It is a design pattern that allows you to make copies of an object without needing to know the underlying type. While I can see how a chatbot thought it was relevent, it's overkill here. Simple polymorphism is all that's needed.

While I understand that the question contains a requirement for a copy, the code presented makes me think that it was a misunderstanding or misstated requirement.

发布评论

评论列表(0)

  1. 暂无评论