Avoiding object slicing within abstractions and their derivations I've created a rudimentary example of the issue I'm inquiring about: Object slicing. I followed a post on Stack Overflow which provided a similar template - and for all intents and purposes, this works. I was just wondering if this would get me into a trouble longer down the line? Is there anything here that jumps out at you as dangerous and/or wrong?

The heart of the logic is in main(). I'm pushing a Circle object into a Shapes* vector, then push the new Shapes* element into a Circle vector. The issue I had WITHOUT the assignment operators, was that the compiler couldn't decide how to convert Shape* to Circle.

Please let me know if there are any glaringly obvious improvements.

class Shape {
public:
    virtual Shape&amp; operator=(const Shape&amp; s) {
        assign(s);
        return *this;
    }

    virtual std::string getName() = 0;
    virtual         int getEdges() = 0;



protected:
    std::string name;
    int         edges;

    void assign(const Shape&amp; s) {
        this-&gt;name = s.name;
        this-&gt;edges = s.edges;
    }
};

class Circle : public Shape {
private:
    int radius;
public:
    Circle() { name = "Circle"; edges = 1; }
    Circle(int rad) { name = "Circle"; edges = 1; radius = rad; }

    virtual Circle&amp; operator=(const Shape&amp; s) {
        if (const Circle* c = dynamic_cast&lt;const Circle*&gt;(&amp;s))
            assign(*c);
        else{
            std::cout &lt;&lt; "BAD ASSIGNMENT IN CIRCLE.";
                    //THROW ERROR HERE INSTEAD OF THE ABOVE COUT
            }
        return *this;
    }

    std::string getName() { return name; }
            int getEdges() { return edges; }
            int getRadius() { return radius; }
           void setRadius(int r) { radius = r; }

protected:
    void assign(const Circle&amp; c) {
        Shape::assign(c);
        this-&gt;radius = c.radius;
    }

};

int main() {
    std::vector&lt;Shape*&gt; shapes;
    std::vector&lt;Circle&gt; circs;
    Circle c2(5); //Creates a circle with 5 for the radius.

    shapes.push_back(&amp;c2); //Pushing the 5-radius circle into the Shapes* vector
    Circle c3; //Creates a circle with default constructor (which does NOT define radius)
    c3 = *shapes[0]; //Now, the overloaded assignment operator. Look at Circle::assign(const Shape&amp;) function
    circs.push_back(c3); //We push our newly assigned circle to our Circle vector
    std::cout &lt;&lt; "c3 radius: " &lt;&lt; circs[0].getRadius(); //This will be 5!
}
Allowing a Shape to be assigned to a circle does not sound like it is very safe. I suppose there are situations where it can be used but I would advice against it in the general case
Also I would use Curiously Recurring Template Pattern to get around having to write the assignment operator-er in every derived class.
class Shape
{ // STUFF
};

template&lt;typename T&gt;
class ShapeCopyable: public Shape
{
    T&amp; operator=(const Shape&amp; s)
    {
        T const&amp; c = dynamic_cast&lt;T const&amp;&gt;(s);  // Throws on bad cast.
        assign(c);

        return *this;
    }
};
class Circle: public ShapeCopyable&lt;Circle&gt;
{
    // Stuff
};

In addition to what @Chris Jester-Young covered (so everything he said):
The Base class of a hierarchy with virtual methods should also have a virtual destructor. Otherwise you are going to start having issues with memory management when you start dynamically creating objects on the fly.
class Shape {
public:
    virtual ~Shape() {}

Don't define the copy constructor or assignment operator if nothing special needs to be done. Let the compiler generated version do the work for you. Unless you have something special in the destructor or have RAW pointer members this is unlikely. PS you should not have RAW pointer members either.
    // Remove this:
    virtual Shape&amp; operator=(const Shape&amp; s);

Don't like getters/setters. They break encapsulation and expose the internal implementation of the object. You should consider why you need to pass the state of the object out (is the function that uses the values really a member function).
Interactions on the object should usually be done internally to the object. This will get rid of most of your needs for getters and setters. If you still have a need after the analysis fine.
    // Don't like them.
    // Make but sometimes you need them but minimze your
    // exposure by using methods that manipulate the object rather
    // than moving the manipulation to an external actor.
    std::string getName() { return name; }
            int getEdges() { return edges; }
            int getRadius() { return radius; }
           void setRadius(int r) { radius = r; }

A vector of pointers. Red flag who owns the pointers.
    std::vector&lt;Shape*&gt; shapes;

A'hh you are pushing stack objects onto the vector.
    shapes.push_back(&amp;c2);

Maybe it should be a vector of reference wrapper to Shape std::vector&lt;std::reference_wrapper&lt;Shape&gt;&gt;.
Getting a circle from a shape.
    c3 = *shapes[0];

Not sure that should really happen. The interface to Shape should be how you manipulate the object. Needing to convert the object to a circle seems unnecessary in most situations (you should probably have virtual functions that do the actions you need). Sometimes things have unique actions then you need to convert them back.But in these situations just use dynamic_cast directly (this also shows you are doing the conversion very explicitly).
Based on comments below:

I will need to pull name's and description's to display to the user

Its easier to ask the object to display itself rather the pull details about the object and then display them.
The simplist example is just printing information to a stream:
// Original Way
Circle    c(5);
std::cout &lt;&lt; &quot;Circle: &quot;  &lt;&lt; c.getName() 
          &lt;&lt; &quot; Edges: &quot;  &lt;&lt; c. getEdges 
          &lt;&lt; &quot; Radius: &quot; &lt;&lt; c.getRadius()
          &lt;&lt; &quot;\n&quot;;

// What I would do is make the serialization of the object to a stream
// a member method that does the work.

class Shape
{
    virtual void print(std::ostream&amp; stream) const = 0; // Can print a Shape
                                                  // Must be specialized for 
                                                  // Specific types.

    friend std::ostream&amp; operator&lt;&lt;(std::ostream&amp; stream, Shape const&amp; shape)
    {
        shape.print(stream);
        return stream;
    }
};

// Now you print like this:
Circle    c(5);
std::cout &lt;&lt; c &lt;&lt; &quot;\n&quot;;

You don't need to expose the members just because you need serialization. Doing it this ways adds consistency so that all objects of a particular type are serialized in the same way. If you need to change what it looks like you have a single place in the code that needs modifying.
We can extend the above to other graphical representations.
struct DisplayInterface
{
     // This is an interface for drawing shapes on a display.
     // You can imagine it has the standard drawing functions.
     // But imagine a base set.
     void drawLine(Pt const&amp; tr, Pt const&amp; bl);
     void drawCircle(Pt const&amp; tr center, unsigned int radius);
     void drawPolygon(std::vector&lt;Pt&gt; const&amp; pts);
     void drawText(Pt const&amp; bl, unsigned int fontSize, std::string const&amp; str);
};

// Now in your drawing code you pass the DisplayInterface to the object
// So it can draw itself. So usually the drawing system provides an
// interface that all drawable objects need to implement.

struct Drawable
{
    virtual void display(DisplayInterface&amp; di) = 0;
};

class Shape: public Drawable  // All shapes are drawable
{                             // But each shape must implement
                              // Its own display()
};

class Circle: public Shape
{
     virtual void display(DisplayInterface&amp; di)
     {
          di.drawCircle(Pt(0,0), radius);
          di.drawText(Pt(0,15), 12, name);
     }
};