简体   繁体   中英

Smart pointers for set of abstract objects?

I'm relatively new to programming and recently, while I was doing the assignment I came across a problem that I'm not sure how to cope with. I have an abstract class called IPrintable and it represents printable objects. Class UnorderedSet is an unordered set of IPrintable objects, it is actually a dynamically allocated array of pointers to the IPrintable objects. The problem is that this implementation bounds lifetime of objects that exist somewhere in memory and "elements" in my set. I'm aware that when those objects get destructed my pointers will point to the deallocated memory block. Can someone tell me how to manage it? The only idea that I can think of is to use a dynamically allocated array of shared smart pointers, but I'm not sure how to do that. Maybe something like this?

std::shared_ptr<std::shared_ptr<ns1::IPrintable>> set ; But how to get shared_ptr from the array? Can I use set[index] ?

    class UnorderedSet:virtual public IPrintable
        {
            int* n;
            int* i;
            IPrintable** set;
    public:
    UnorderedSet(int n):n(new int(n)),i(new int(0)),set(new IPrintable*[n])
    {
        for (int j = 0; j < n; j++)
            set[j] = nullptr;  // I cant have set[j]= new IPrintable 
                                  because its abstract class
    }
    ...
    void insert(IPrintable* const iprt)
    {
        if (*i == *n)
            this->add_capacity();// extends capacity
        set[*i] = iprt;
        (*i)++;
    }
    ...
    }
class IComparable:public IPrintable
    {
        virtual double compare_criteria()const = 0;
    public:
        virtual ~IComparable()=default;
        std::strong_ordering operator<=>(const IComparable&)const;
    };
class Complex: public IComparable
    {
        double *re, *im;
    public:
...}
int main()
{
Complex* z=new Complex(1,1);
UnorderedSet uset(4);
uset.insert(z);
z->~Complex();// uset.set[0] points to the deallocated memory block
return 0;
}

You can create a container that holds a smartpointer type just like you would with any other type eg:

std::vector<std::shared_ptr<IPrintable>> myvec;

And then you would be able to access it by array indexing:

myvec[0];

A shared_ptr uses reference counting to know when it should be deleted as the deleter will be called when it reaches 0 references.

A unique_ptr is used to represent an item which you want only 1 reference to.

You have to take certain care with unique_ptr in containers as a unique_ptr is non-copyable.

Eg you could create the pointer within the vector:

std::vector<std::unique_ptr<IPrintable>> myvec;
myvec.emplace_back(std::make_unique<IPrintable>());

And ensure you access the elements by reference rather than taking a copy eg:

for(auto& printable : myvec) {
    //use printable unique_ptr
}

Also:

z->~Complex();// uset.set[0] points to the deallocated memory block

You shouldn't be manually calling the destructor but rather call delete on z . If you want your UnorderedSet to take ownership of the allocated memory, you can have your UnorderedSet call delete on it's items in the UnorderedSet 's destructor or when an item is removed from the set.

If you are willing to not insist on an unordered set of IPrintable then the example below might give some guidance. The z element lives only for a short period and is automatically destroy but still detected by the std::weak_ptr when iterating over the container.

I deliberately avoid using std::make_shared in this example since a control block created by std::make_shared will also hold the allocation area for the object in question. If the weak_ptr lives a lot longer than the original shared_ptr then that memory will not be reclaimed until the last weak_ptr is destroyed.

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

class IPrintable
{
public:
   virtual ~IPrintable() = default;
   virtual void print()  = 0;
};

class Real : public IPrintable
{
   int re{};
public:
   Real(int) {}
   void print() override { std::cout << "Real::print()\n"; }
};

class Complex : public IPrintable
{
   double *re{}, *im{};
public:
   Complex(double, double = double{}) {}
   void print() override { std::cout << "Complex::print()\n"; }
};

int main()
{
   std::vector<std::weak_ptr<IPrintable>> printables;
   std::shared_ptr<Real> r(new Real(3));
   {
      std::shared_ptr<Complex> z(new Complex(4, 5));
      printables.push_back(z);
   }
   printables.push_back(r);
   for(auto& wp : printables)
      if(auto sp = wp.lock())
         sp->print();
      else
         std::cout << "Lost entry\n";
}

This example should print:

Lost entry
Real::print()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM