Please share your Opinion on a C++ Syllabus

I’m late to the party here but the future of C++ if there is one is going to be based on ranges, concepts, memory safety techniques, writing code for local reasoning and efficient use of the standard library (i.e. making use of stdlib algorithms to solve exercises).

There’s a lot of cruft in C++ and cutting through that cruft to what’s relevant today is going to be important. I would look to build a course around that. That would be super relevant even for someone like me with 20+ years of C++ experience.

A bit of memory safety techniques will be covered with the smart pointer concept. It is already written and has been waiting for approval for a while. Maybe I should just publish it and see the reaction?

There’s a lot to cover with smart pointers as there’s a lot of confusion about what guarantees smart pointers provide (and don’t provide).
At the least any reasonable curriculum should cover what is discussed in this

To an extent this touches on local reasoning as well as it is now considered good practice not to pass shared pointers directly as a function argument unless the callee needs to manipulate the shared pointer for some reason.

Of course the incremental approach to building something has a lot to say for it. So show what you’ve got! Thks.

Here is the exercism cpp concept for smart pointers.

This is the upcoming PR with a concept exercise..

Please share your opinion :slight_smile:

I appreciate the effort. The problem you have with this is that chatgpt spins out a lot of this in response to the prompt “teach me about smart pointers”

Lots of ChatGPT Output

C++ smart pointers are a type of pointer that automatically manages the memory they point to, helping to prevent memory leaks and dangling pointers. They are part of the C++ Standard Library, introduced in C++11, and provide safer and more efficient alternatives to raw pointers.

There are three main types of smart pointers in C++:

  1. std::unique_ptr
  2. std::shared_ptr
  3. std::weak_ptr

Let’s go over each of these in detail:

1. std::unique_ptr

std::unique_ptr is a smart pointer that owns a dynamically allocated object exclusively. This means that only one unique_ptr can point to a particular resource at any given time. When the unique_ptr goes out of scope, it automatically deletes the object it points to.

  • Features:

    • Ensures exclusive ownership, preventing multiple smart pointers from owning the same resource.
    • Non-copyable but movable, meaning you can transfer ownership using std::move().
  • Example:

    #include <memory>
    
    class MyClass {
    public:
        void say_hello() { std::cout << "Hello, World!" << std::endl; }
    };
    
    int main() {
        // Creating a unique_ptr
        std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
        ptr->say_hello();
        
        // Ownership is transferred to another unique_ptr
        std::unique_ptr<MyClass> ptr2 = std::move(ptr);
        // Now, ptr is null, and ptr2 owns the resource
    }
    
  • Key Point: Once ptr is moved to ptr2, ptr is in a null state. Only ptr2 can access the resource.

2. std::shared_ptr

std::shared_ptr is a smart pointer that allows multiple pointers to share ownership of the same object. It keeps track of how many shared_ptr instances point to the same resource using a reference count. The object is destroyed when the last shared_ptr is destroyed or reset.

  • Features:

    • Allows multiple shared_ptr instances to own the same resource.
    • The reference count is automatically managed, and the object is deallocated when the last shared_ptr goes out of scope or is reset.
  • Example:

    #include <memory>
    
    class MyClass {
    public:
        void say_hello() { std::cout << "Hello, World!" << std::endl; }
    };
    
    int main() {
        // Creating a shared_ptr
        std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
        ptr1->say_hello();
        
        // Creating another shared_ptr to the same object
        std::shared_ptr<MyClass> ptr2 = ptr1;
        
        // Both ptr1 and ptr2 now share ownership of the same object
        // The object will be destroyed when both ptr1 and ptr2 are out of scope
    }
    
  • Key Point: The resource is only deleted when the last shared_ptr goes out of scope, thanks to the reference count.

3. std::weak_ptr

std::weak_ptr is a smart pointer that doesn’t contribute to the reference count of a shared_ptr. It is used to break circular references between shared_ptr instances. A weak_ptr can be converted to a shared_ptr, but only if the object it points to has not been destroyed.

  • Features:

    • Doesn’t affect the reference count.
    • Allows you to observe an object managed by a shared_ptr without preventing it from being deleted.
  • Example:

    #include <memory>
    
    class MyClass {
    public:
        void say_hello() { std::cout << "Hello, World!" << std::endl; }
    };
    
    int main() {
        std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
        std::weak_ptr<MyClass> weakPtr = ptr1;  // weak_ptr does not affect reference count
        
        if (auto shared = weakPtr.lock()) {  // Attempts to convert to shared_ptr
            shared->say_hello();
        } else {
            std::cout << "Object has been deleted." << std::endl;
        }
    }
    
  • Key Point: The lock() method converts a weak_ptr into a shared_ptr if the object is still alive, or returns nullptr if it has been destroyed.

Why Use Smart Pointers?

  • Automatic Resource Management: Smart pointers ensure resources (memory, file handles, etc.) are automatically cleaned up when they are no longer needed. This reduces the risk of memory leaks.

  • Prevents Dangling Pointers: Smart pointers automatically delete the object they point to when they go out of scope, ensuring no invalid memory access happens.

  • Safer Ownership Semantics:

    • unique_ptr: For exclusive ownership.
    • shared_ptr: For shared ownership.
    • weak_ptr: To observe an object without affecting its lifetime.

When to Use Each Smart Pointer:

  • Use unique_ptr when you want exclusive ownership of a resource (e.g., single-owner containers, factory functions).
  • Use shared_ptr when multiple parts of the code need to share ownership of a resource (e.g., managing shared data, cache).
  • Use weak_ptr to break circular references when using shared_ptr, or when you want to observe an object without affecting its lifetime (e.g., observer patterns).

Key Differences Between Smart Pointers:

Feature std::unique_ptr std::shared_ptr std::weak_ptr
Ownership Exclusive (one owner) Shared ownership No ownership
Reference Count No (not tracked) Yes (tracked) No (does not affect)
Memory Deletion Automatic when out of scope Automatic when last shared_ptr is destroyed Does not manage memory directly
Copyable No (but movable) Yes Yes

Conclusion:

Smart pointers simplify memory management and help prevent issues such as memory leaks and dangling pointers. They are essential tools in modern C++ programming for robust and safe resource management.

So the challenge will be to present material that is beyond what chatgpt provides. I would focus on the safety guarantees that smart pointers do and don’t provide and the local reasoning aspects of shared pointers.

I’m confused.
Did you write that up, or is this a chatgpt response? I do not really see any response to the exercise and the already existing written text from the post above.

That’s a chatgpt response. My point is that general knowledge on smart pointers is easily obtainable. What is less visible is guidance on proper usage and the minutiae of the actual guarantees of c++ smart pointers.

It is perfectly fine to have some general info on smart pointers but I think a focus on the usage and guarantees and non-guarantees of shared pointers is what will add value to exercism.

Once again, I appreciate the work you are doing. I am just trying to point out with my 20+ years in C++ what I think would be of most value on this topic.

chatgpt can generate general exercises ad-nauseum also:

Lots of ChatGPT Output "write me an exercise through which I can learn about smart pointers"

Certainly! Below is an exercise that will help you learn about C++ smart pointers, specifically std::unique_ptr, std::shared_ptr, and std::weak_ptr. By following this exercise, you’ll get hands-on practice with smart pointers and their key features.

Exercise: Smart Pointers in C++

Goal:

Implement a simple memory management system where you manage a set of Book objects using smart pointers. You’ll practice creating and manipulating unique_ptr, shared_ptr, and weak_ptr.

Step 1: Define the Book class

Start by defining a Book class with a constructor, a destructor, and a method to display the book’s information.

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

class Book {
public:
    Book(const std::string& title, const std::string& author)
        : title(title), author(author) {
        std::cout << "Book created: " << title << " by " << author << std::endl;
    }

    ~Book() {
        std::cout << "Book destroyed: " << title << " by " << author << std::endl;
    }

    void display() const {
        std::cout << "Title: " << title << ", Author: " << author << std::endl;
    }

private:
    std::string title;
    std::string author;
};

Step 2: Using std::unique_ptr

Create a Book using std::unique_ptr. You should create a unique_ptr for a book, display its information, and ensure that it is automatically deleted when it goes out of scope.

void useUniquePtr() {
    std::unique_ptr<Book> uniqueBook = std::make_unique<Book>("1984", "George Orwell");
    uniqueBook->display();

    // uniqueBook will be automatically destroyed when it goes out of scope
}

Exercise:

  • Run the program and verify that the book is created and destroyed properly.

Step 3: Using std::shared_ptr

Now, create a Book using std::shared_ptr. You will create multiple shared pointers pointing to the same Book object. Ensure that the object is deleted when the last shared pointer goes out of scope.

void useSharedPtr() {
    std::shared_ptr<Book> sharedBook1 = std::make_shared<Book>("To Kill a Mockingbird", "Harper Lee");
    std::shared_ptr<Book> sharedBook2 = sharedBook1;  // shared ownership

    sharedBook1->display();
    sharedBook2->display();

    // sharedBook1 and sharedBook2 will be automatically destroyed when they go out of scope
}

Exercise:

  • Create a second shared_ptr that shares ownership of the same book.
  • Observe the reference count behavior (you can print the reference count if you want).
  • Check that the book is destroyed only after the last shared pointer goes out of scope.

Step 4: Using std::weak_ptr

Use a std::weak_ptr to observe a Book managed by std::shared_ptr, but without affecting its reference count. The purpose of weak_ptr is to avoid circular references.

void useWeakPtr() {
    std::shared_ptr<Book> sharedBook = std::make_shared<Book>("The Catcher in the Rye", "J.D. Salinger");
    std::weak_ptr<Book> weakBook = sharedBook;  // weak_ptr does not affect reference count

    if (auto lockedBook = weakBook.lock()) {
        lockedBook->display();
    } else {
        std::cout << "The book has been deleted." << std::endl;
    }

    // The book will be destroyed after the last shared_ptr goes out of scope
}

Exercise:

  • Use weak_ptr to observe the shared_ptr-managed book.
  • Try to lock the weak_ptr and display the book’s information.
  • After the last shared_ptr is destroyed, try locking the weak_ptr again to observe that the book is no longer available.

Step 5: Putting it All Together

Now, in your main function, use the functions to demonstrate the three types of smart pointers:

int main() {
    std::cout << "Using unique_ptr:\n";
    useUniquePtr();
    
    std::cout << "\nUsing shared_ptr:\n";
    useSharedPtr();
    
    std::cout << "\nUsing weak_ptr:\n";
    useWeakPtr();

    return 0;
}

Step 6: Test and Observe the Output

Run the program, and observe the following:

  1. unique_ptr: You should see that the book is created and destroyed automatically, with no extra output beyond the creation and destruction messages.
  2. shared_ptr: You’ll observe that the book is created and destroyed once the last shared_ptr goes out of scope.
  3. weak_ptr: You’ll see how weak_ptr can observe an object without affecting its lifetime. When the shared_ptr is destroyed, the book is deleted, and you won’t be able to lock the weak_ptr anymore.

Additional Challenges:

  1. Reference Counting: Print the reference count of a shared_ptr at different points in your program using std::shared_ptr::use_count().
  2. Circular References: Try creating a scenario where two shared_ptrs reference each other (a circular reference). Use weak_ptr to fix the issue and prevent a memory leak.
  3. Custom Deleter: Experiment with using a custom deleter when creating smart pointers (e.g., deleting an object via a specific function instead of delete).

Conclusion

By completing this exercise, you’ll have a deeper understanding of how smart pointers work in C++, including ownership semantics and memory management. You’ll also gain experience in how unique_ptr, shared_ptr, and weak_ptr can be used to manage resources safely and efficiently in a C++ program.

Would it be possible to have some sort of framework through which chatgpt generated exercises can be added to exercism?

I really don’t want to sound too critical but we do live in the era of chatgpt and some attention needs to given to providing value beyond what chatgpt provides. Otherwise we are all publishing phone books in the era of the internet.

Thanks for the thoughts.

If maintainers want to use ChatGPT to help generate content and exercises, then they definitely can. I’m sure that they (as do I) often use it to help.

Exercism exists as an education resource to help people get quick intros then practical hands-on exercises to use a topic. We then follow that up with mentoring where we can dig into deeper details.

Exercise has always sat in an ecosystem of thousands of other websites and books that explain the same concepts. ChatGPT adds yet another place to get overviews of topics, but that doesn’t make it very unique.

We don’t exist to supplement the other resources out there. We exist to provide a first introduction to different topics and ideas in a fun, hands-on way. We try and have exercises that have colour and flavour, not just where you’re adding a line or two to make tests pass. That’s our philosophy/ethos as a site. Some people enjoy that - others find it annoying and would rather get a ChatGPT-style explanation and exercise - there’s room for both IMO, just like there has always been with different authors/websites in the past.

Definitely appreciate hearing this. Probably copy/pasting masses of ChatGPT doesn’t really help us separate out what you’re trying to suggest we focus on though.

I think what you’re saying is that “there’s a lot of confusion about what guarantees smart pointers provide” and that it would be useful for us to explain that. That definitely sounds like a fun exercise in itself (specifically focussed on smart pointer guarantees), but that wouldn’t replace the previous exercises that introduce the topics to people who are learning C++ or memory management for the first time (which is most people using Exercism’s syllabuses)

1 Like

Totally agree on the value of exercism. The problem with C++ is that a lot of beginner material on C++ extols the cruft of C++. C++ suffers from a deficit of local reasoning. Beginner examples do things like

void this_function_swaps(std::unique_ptr& a,
std::unique_ptr& b) {
std:::swap(a, b);
}

While this has some didactic value it begins to ingrain non-local reasoning into newbies right from the start.

I’m enjoying my time on exercism and I appreciate the effort of the maintainers. Just my 2 cents that it would be great to build in what the future of C++ looks like into the content.

1 Like