0

I want to loop through an array of pointers to an abstract class to find an "empty" slot, that is to check whether an element points to an object of a derived class or not. My approach is to create the array and set each element to nullptr. Then, I can check if the element is nullptr.

This works, but is there a better way? Edit: Can I check for the first "empty" element in the array of pointers to an abstract class (in which derived classes will periodically be constructed and pointed to by the array, rendering that element not "empty"), without assigning each element to nullptr upon setting up the array and then checking for nullptr as a way to check if the element is "empty"? In other words, can I directly check whether the element points to a constructed base class or not?

Cat** catArray = new Cat*[200];
for(int i = 0; i < 200; i++){
   catArray[i] = nullptr;
}

for(int i = 0; i < 200; i++){
   if(catArray[i] == nullptr){ //edited, was typo as "!="
      AddRealCat(...);
      break;
   }
}      

I wonder if there's an easier way to do this, to check whether an element in an array of pointers to an abstract class points to an object of a derived class or is just an abstract pointer, without setting the element to nullptr. Like, is there a bool IsObject(ObjectType* ptr) or something in the standard library?

And, I wonder if setting each element to nullptr poses any potential problems, other than the computing cost of looping through the array and setting the elements to nullptr.

Kaleb Coberly
  • 420
  • 1
  • 4
  • 19
  • 3
    So what is it? Do you want to find empty spots (where `nullptr` is absolutely reasonable) or find out if a pointer points to an object of a specific class (where you would use something like `if(dynamic_cast(catArray[i]))`)? – Nico Schertler Sep 23 '19 at 01:46
  • you could store a `size_t` indicating where is the last `Cat` was stored. Or you could use `std::vector` or any other std container instead of array. And even you could use `std::optional` which is designed to address these kinda issues. – The Moisrex Sep 23 '19 at 02:13
  • There is no such thing as an "abstract pointer" in your setup. Could you describe what you mean by that phrase? Also, if an element is not set to a value, then checking its value is undefined behavior. (That's why pointers should always be set to some value; the `nullptr` value carries the semantics "I do not point to anything".) – JaMiT Sep 23 '19 at 02:59
  • There is any reason to no to use a `std::vector` ? or even better a `std::vector>` ? – Moia Sep 23 '19 at 06:54
  • `Cat*[i] = nullptr;` is a syntax error, it certainly does not "work" – M.M Sep 23 '19 at 07:36
  • Nico Shertler, yes. I want to find the first empty slot. – Kaleb Coberly Sep 24 '19 at 00:06
  • JaMiT, thanks for correcting my nomenclature. I mean a pointer of an abstract class type. In this case, Cat is an abstract class. – Kaleb Coberly Sep 24 '19 at 00:08
  • M.M, you're right! That's not the syntax I'm using in my actual project. I messed it up when I stripped it down to isolate the question here. Let me see if I can edit the question. – Kaleb Coberly Sep 24 '19 at 00:11
  • Okay, fixed it, I think. But, I'm getting a warning in my project around this code. It appears the writeable size is some object size times 8 bytes, whereas 16 bytes might be written. Not sure if this is leading to my current read access error appearing to have something to do with Right_Size in xstring? – Kaleb Coberly Sep 24 '19 at 00:21
  • To those suggesting that I use a vector, I agree, and this is a school project which requires an array. Yet, while it is just a school project, dealing with arrays of pointers seems to have real-world application when dealing with legacy code and just to understand how memory management works in C++. Thanks to everyone chiming in! – Kaleb Coberly Sep 24 '19 at 00:24

2 Answers2

0

You would have to use dynamic_cast to cast a base class pointer to a derived class pointer. dynamic_cast performs type safe down casting.

If the result of dynamic_cast is not nullptr then the cast has been successful. Otherwise no derived class pointer can be obtained from the pointer.

You would have to do like this:

Cat *pCat = dynamic_cast<Cat*>(catArray[i]);
if (pCat)
{
    AddRealCat(...);
    break;
}

where catArray is an array of base class pointers.

Update

I think there's an error with creating an array of real cat objects:

for(int i = 0; i < 200; i++){
   if(catArray[i] != nullptr){
      AddRealCat(...);
      break;
   }
}

Surely you need to check == nullptr since you initialising all array elements to nullptr? I think you need to make the following change:

for(int i = 0; i < 200; i++){
   if(catArray[i] == nullptr){
      catArray[i] = new RealCat();
      break;
   }
}    
jignatius
  • 6,304
  • 2
  • 15
  • 30
  • Thanks, jignatius! I'm going to check this out! – Kaleb Coberly Sep 24 '19 at 00:27
  • I wasn't able to produce the outcome I'm looking for with dynamic_cast. I need to check whether the pointer points to a constructed object or not. – Kaleb Coberly Sep 29 '19 at 02:53
  • thanks! You're right. Yes, this is another case in which I put a bug into the example code when trying to strip it down from the actual code I'm working on. Fixing it above. – Kaleb Coberly Sep 30 '19 at 01:14
0

I guess the real easier way to do that other using dynamic_cast is using std::vector instead of a raw pointer.

Sample code

#include <string>
#include <iostream>
#include <vector>

struct Cat{
    virtual ~Cat() = default;
    virtual void meow() const = 0;
};

struct Meow : Cat{
    void meow() const override{ std::cout << "meow" << std::endl; }
};

int main()
{
    std::vector<Cat*> vec{100};
    vec[1] = new Meow();

    for(auto c : vec){
        if(auto m = dynamic_cast<Meow*>(c)){
            m->meow();
        }
    }

    // don't forget to release memory
    for(auto c : vec){
        delete c;
    }
}

Live Example

Modern version, using smart pointers.

#include <string>
#include <iostream>
#include <vector>
#include <memory>

struct Cat{
    virtual ~Cat() = default;
    virtual void meow() const = 0;
};
struct Meow : Cat{
    void meow() const override{ std::cout << "meow" << std::endl; }
};

int main()
{
    std::vector<std::unique_ptr<Cat>> vec{100};
    vec[1] = std::make_unique<Meow>();

    for(auto&& c : vec){
        if(auto m = dynamic_cast<Meow*>(c.get())){
            m->meow();
        }
    }

    // you don't need to manually clear the memory.
}

Live Example

Moia
  • 2,216
  • 1
  • 12
  • 34