CS374: Programming Language Principles - Modern Language Features

Activity Goals

The goals of this activity are:
  1. To utilize modern language features for working with memory
  2. To explain the concept of scope
  3. To explain the purpose of object references
  4. To time and compare different architectural approaches

The Activity

Directions

Consider the activity models and answer the questions provided. First reflect on these questions on your own briefly, before discussing and comparing your thoughts with your group. Appoint one member of your group to discuss your findings with the class, and the rest of the group should help that member prepare their response. Answer each question individually from the activity, and compare with your group to prepare for our whole-class discussion. After class, think about the questions in the reflective prompt and respond to those individually in your notebook. Report out on areas of disagreement or items for which you and your group identified alternative approaches. Write down and report out questions you encountered along the way for group discussion.

Model 1: C++ Unique Pointers

Questions

  1. What does this code do?
  2. What does this code have in common with traditional pointers? What would you have to do differently if you were creating the pointers yourself?
  3. What do you think std::move does?
  4. What benefits does std::unique_ptr provide?

Model 2: C++ Shared Pointers

Questions

  1. What is the purpose of the extra curly braces inside main?
  2. When resPtr1 goes out of scope, why is the underlying memory still accessible?
  3. In your own words, what is the benefit of a std::shared_ptr?

Model 3: Vectors and the C++ Standard Template Library

Questions

  1. What do you think the auto type means? How do you think it works?
  2. Why is anotherWidgetPtr still accessible after removing it from the vector?

Model 4: C++ Constant Expressions

Questions

  1. Try this code for various values of n and k. Which program is faster?
  2. What do you notice when compiling this program? Why do you think this is? What is the compromise?
  3. What, in your own words, does constexpr do?
  4. What do you think the compiler flag constexpr-steps means?

Smart Pointer Examples: Before and After

Example 1: Using unique_ptr instead of Raw Pointers

Before: Raw Pointers After: unique_ptr
#include <iostream>

class Resource {
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
    void say_hello() const { std::cout << "Hello from Resource\n"; }
};

void raw_pointer_example() {
    Resource* res = new Resource();
    res->say_hello();

    // Simulate transferring ownership (forgetting to delete might cause a memory leak)
    Resource* res2 = res;
    res = nullptr;

    // Resource cleanup
    delete res2; // If we forget this, it causes a memory leak.
}

int main() {
    raw_pointer_example();
    return 0;
}
#include <iostream>
#include <memory>  // For std::unique_ptr

class Resource {
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
    void say_hello() const { std::cout << "Hello from Resource\n"; }
};

void unique_pointer_example() {
    std::unique_ptr<Resource> res = std::make_unique<Resource>();
    res->say_hello();

    // Transferring ownership with std::move
    std::unique_ptr<Resource> res2 = std::move(res);

    // No need to manually delete, as unique_ptr handles this automatically
    // Resource will be destroyed when res2 goes out of scope
}

int main() {
    unique_pointer_example();
    return 0;
}

Example 2: Using shared_ptr instead of Raw Pointers

Before: Raw Pointers After: shared_ptr
#include <iostream>

class Resource {
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
    void say_hello() const { std::cout << "Hello from Resource\n"; }
};

void raw_pointer_shared_example() {
    Resource* res = new Resource();

    // Multiple "owners" of the same resource
    Resource* res1 = res;
    Resource* res2 = res;

    res1->say_hello();
    res2->say_hello();

    // Manually managing cleanup
    delete res1;  // Deleting res1 here makes res2 a dangling pointer
    // delete res2;  // If uncommented, this would cause undefined behavior (double delete)
}

int main() {
    raw_pointer_shared_example();
    return 0;
}
#include <iostream>
#include <memory>  // For std::shared_ptr

class Resource {
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
    void say_hello() const { std::cout << "Hello from Resource\n"; }
};

void shared_pointer_example() {
    std::shared_ptr<Resource> res = std::make_shared<Resource>();

    // Multiple shared_ptr instances share ownership of the same resource
    std::shared_ptr<Resource> res1 = res;
    std::shared_ptr<Resource> res2 = res;

    res1->say_hello();
    res2->say_hello();

    // No need to manually delete; the resource is automatically deleted
    // when the last shared_ptr (res, res1, or res2) goes out of scope
}

int main() {
    shared_pointer_example();
    return 0;
}

Submission

I encourage you to submit your answers to the questions (and ask your own questions!) using the Class Activity Questions discussion board. You may also respond to questions or comments made by others, or ask follow-up questions there. Answer any reflective prompt questions in the Reflective Journal section of your OneNote Classroom personal section. You can find the link to the class notebook on the syllabus.