Assignment Chef icon Assignment Chef
All English tutorials

Programming lesson

C++ Inheritance in Action: Building Dragons from Cows in COP3504C Lab 07

Learn how to implement class inheritance in C++ by extending a Cow class into Dragon and IceDragon classes, inspired by a classic lab assignment. This tutorial covers virtual functions, constructors, and polymorphism with practical code examples.

C++ inheritance C++ polymorphism class hierarchy virtual functions constructor chaining cowsay C++ COP3504C lab 07 C++17 project derived class override specifier dynamic_cast game character hierarchy object-oriented programming C++ tutorial 2026 student programming assignment inheritance example

Introduction: When Cows Learn to Breathe Fire

In the world of object-oriented programming (OOP), inheritance allows you to build new classes from existing ones, reusing and extending functionality. This tutorial walks you through a classic C++ inheritance exercise: converting a Java cowsay utility into C++ and adding mythical creatures. By the end, you'll understand how to create a base Cow class, derive a Dragon class, and further specialize it into an IceDragon class. This mirrors real-world scenarios like building a character hierarchy in a game or modeling different types of AI agents.

Setting Up Your C++17 Project

Before diving into code, ensure your environment is ready. Update your CMakeLists.txt to use C++17:

cmake_minimum_required(VERSION 3.10)
project(cowsay)
set(CMAKE_CXX_STANDARD 17)
add_executable(cowsay cowsay.cpp Cow.cpp Dragon.cpp IceDragon.cpp HeiferGenerator.cpp)

This ensures your compiler supports modern C++ features like std::string_view and improved virtual function handling.

Understanding the Cow Class

The Cow class is your base. It stores a name and an ASCII art image. Key methods include a constructor, getters, and a virtual setImage() function. Marking setImage as virtual allows derived classes to override it if needed. Here's the header:

// Cow.h
#ifndef COW_H
#define COW_H
#include <string>
using namespace std;
class Cow {
public:
    Cow(const string& name);
    string getName() const;
    string getImage() const;
    virtual void setImage(const string& image);
private:
    string name_;
    string image_;
};
#endif

Implementation is straightforward. The constructor initializes the name, and setImage assigns the image string.

Creating the Dragon Class

Now, let's make a Dragon that inherits from Cow. The Dragon class adds one key method: canBreatheFire(), which returns true by default. This is a classic example of C++ polymorphism, where the base class pointer can call derived class methods.

// Dragon.h
#ifndef DRAGON_H
#define DRAGON_H
#include "Cow.h"
class Dragon : public Cow {
public:
    Dragon(const string& name, const string& image);
    bool canBreatheFire() const;
};
#endif

Notice the constructor takes both name and image, and passes them to the Cow constructor. The canBreatheFire method simply returns true.

Specializing with IceDragon

The IceDragon class extends Dragon. It overrides canBreatheFire() to return false. This demonstrates virtual function overriding and class hierarchy.

// IceDragon.h
#ifndef ICEDRAGON_H
#define ICEDRAGON_H
#include "Dragon.h"
class IceDragon : public Dragon {
public:
    IceDragon(const string& name, const string& image);
    bool canBreatheFire() const override;
};
#endif

The override specifier is optional but recommended—it helps catch mistakes at compile time.

Implementing the Constructors

Each derived class constructor must call its base class constructor. For Dragon:

// Dragon.cpp
#include "Dragon.h"
Dragon::Dragon(const string& name, const string& image) : Cow(name) {
    setImage(image);
}
bool Dragon::canBreatheFire() const { return true; }

For IceDragon:

// IceDragon.cpp
#include "IceDragon.h"
IceDragon::IceDragon(const string& name, const string& image) : Dragon(name, image) {}
bool IceDragon::canBreatheFire() const { return false; }

The HeiferGenerator: Creating Objects

The provided HeiferGenerator class returns a vector of Cow* pointers. It includes cows, dragons, and ice dragons. You'll need to use dynamic_cast to check if a pointer actually points to a Dragon object. This is a common pattern in C++ inheritance when dealing with heterogeneous collections.

Dragon* dragonPtr = dynamic_cast<Dragon*>(cowPtr);
if (dragonPtr) {
    cout << (dragonPtr->canBreatheFire() ? "can" : "cannot") << " breathe fire." << endl;
}

Building the cowsay Driver

Your cowsay.cpp must parse command-line arguments. Use getopt or a simple loop. The -l flag lists available cows. The -n flag selects a specific cow. For dragons, after printing the message, output the fire-breathing status. Here's a skeleton:

int main(int argc, char* argv[]) {
    vector<Cow*> cows = HeiferGenerator::getCows();
    // parse arguments...
    // find cow by name...
    // print image...
    // if dragon, check canBreatheFire...
}

Testing Your Program

Compile and run. Sample output should match exactly. For example:

./cowsay -n dragon Firey RAWR
...
This dragon can breathe fire.

If you get a segfault, check your memory management. Since HeiferGenerator returns raw pointers, ensure you don't delete them twice. Consider using unique_ptr in real projects.

Trend Connection: Gaming Character Hierarchies

In popular games like Elden Ring or Genshin Impact, characters often inherit abilities from base classes. For instance, a Dragon base class might have a breatheFire() method, while IceDragon overrides it to breatheIce(). This lab mirrors that design pattern, preparing you for game development or simulation software.

Common Pitfalls and Tips

  • Missing virtual destructor: If you plan to delete objects through base pointers, declare a virtual destructor in Cow.
  • Constructor chaining: Always pass parameters correctly to base classes.
  • Case sensitivity: The cow names are case-sensitive; match exactly.
  • CMakeLists.txt: Ensure C++17 is set; otherwise, override may not be recognized.

Conclusion

You've now implemented a multi-level inheritance hierarchy in C++. The Cow class serves as a base, Dragon adds fire-breathing, and IceDragon overrides it. This lab teaches virtual functions, constructors in inheritance, and polymorphism. These concepts are essential for building extensible software, from simple utilities to complex AI systems.

For further practice, try adding a FireDragon class that breathes fire more intensely, or a MountainDragon with a different ability. The possibilities are endless!