Schedule‎ > ‎

14A Classes and Objects Cont.


Working with Objects and Files

Learner Outcomes

At the end of the lesson the student will be able to:

  • Pass objects to functions
  • Return objects from functions

^ top

Revisiting Objects and Classes

  • Previously we discussed how to code classes to create objects

Objects

  • Recall that an object in C++ is a location in memory containing a structured collection of variables and functions defined by its class
  • As an example, here is a "product" object in memory:
    Milk
    3.95
    functions
  • The example object has two pieces of data, a name and a price, structured one after the other in memory
  • In addition, the object has access to associated functions
  • To define the data structure of objects, we write a class

Classes

  • A class is a program-code "template" for creating objects
  • Objects are then a particular instance of a class, meaning an object has particular values
  • The particular values are stored in memory as defined by the class template
  • The data values are structured in the order defined by the class:
    class Product {
    private:
        string name;
        double price;
    ...
    }
    

Information Hiding

  • Remember that we always code our class member variables as private
  • The keyword private restricts access to member functions only
  • Keeping member variables private is important so we can make design changes

Object Interface

  • To access private data, we code public member functions like:
    public:
        string getName() const;
        double getPrice() const;
        void setName(string newName);
        void setPrice(double newPrice);
        void print() const;
    
  • These public functions are the interface to our class
  • The interface is how we communicate with and use our objects

Constructing Objects

  • To create objects from the class, we construct an object like:
    Product milk;
  • When the object is created, memory is allocated for the class variables
    Name: ""
    Price: undefined
  • However, the memory is uninitialized
  • We can use the set functions to assign the memory values:
    milk.setName("Low fat milk");
    milk.setPrice(3.95);
    
  • However, this is cumbersome and provides no guarantee that the programmer using our class will completely initialize the object data
  • A better solution is to code constructor functions

Constructor Functions

  • A constructor is a special type of function whose purpose is to initialize member variables
  • Whenever an object is created from a class, a constructor is always called automatically
  • A default constructor must set the member variables to default values:
    Product::Product() {
        name = "none";
        price = 0.0;
    }
    
  • Even though we should always code a default constructor, it is convenient to code other constructors like:
    Product::Product(string newName, double newPrice) {
        setName(newName);
        setPrice(newPrice);
    }
    
  • This lets us construct an object and initialize data members at the same time:
    Product milk("Low fat milk", 3.95);
    
  • When we are done, we have a modular grouping of variables and functions

Example Program with a Class Declaration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <iostream>
using namespace std;

class Product {
public:
    Product();
    Product(string newName);
    Product(string newName, double newPrice);
    double getPrice() const;
    void setPrice(double newPrice);
    void read();
    void print() const;
private:
    string name;
    double price;
};

Product::Product() {
    name = "Unknown";
    price = 0.0;
}

Product::Product(string newName) {
    name = newName;
    price = 0.0;
}

Product::Product(string newName, double newPrice) {
    name = newName;
    price = newPrice;
}

double Product::getPrice() const {
    return price;
}

void Product::setPrice(double newPrice) {
    price = newPrice;
}

void Product::read() {
    cout << "Enter the name of the product: ";
    cin >> ws;
    getline(cin, name);
    cout << "Enter the price for a " << name << ": ";
    cin >> price;
}

void Product::print() const {
    cout <<  name << " @ " << price << endl;
}

// For testing
int main() {
    Product milk;
    Product bread("Rye Bread");
    bread.setPrice(2.99);
    Product cheese("Cheddar", 6.75);

    cout << "Enter the milk product data\n";
    milk.read();

    milk.print();
    bread.print();
    cheese.print();

    double price = milk.getPrice();
    cout << "The current price of milk is $" << price << endl;
    cout << "Enter the new price: ";
    double newPrice = 0;
    cin >> newPrice;
    milk.setPrice(newPrice);
    milk.print();

    return 0;
}

Check Yourself

  1. True or false: a class contains (encapsulates) both variables and functions.
  2. To allow only member functions and constructors of an object to access a member variable, use the keyword ________.
  3. True or false: good programming practice is to set the accessibility of all member variables to private.
  4. Public functions are the ________ of a class.
  5. True or false: the purpose of a constructor is to initialize all the member variables.

^ top

Returning Objects from Functions

  • Objects may be returned from functions
  • When returned, C++ simply makes a copy of the object
  • We make a simple non-member function to demonstrate this technique:
    Product makeProduct() {
        string name;
        cout << "Enter a product name: ";
        cin >> name;
        double price;
        cout << "Enter the price for a "
             << name << ": ";
        cin >> price;
        Product newProd(name, price);
        return newProd;
    }
    
  • We show returning an object in productapp.cpp below

Returning an Object: productapp.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <iostream>
#include <vector>
using namespace std;

class Product {
public:
    Product();
    Product(string newName, double newPrice);
    string getName() const;
    double getPrice() const;
    void setName(string newName);
    void setPrice(double newPrice);
    void print() const;
private:
    string name;
    double price;
};

Product::Product() {
    name = "none";
    price = 0.0;
}

Product::Product(string newName, double newPrice) {
    setName(newName);
    setPrice(newPrice);
}

string Product::getName() const {
    return name;
}

double Product::getPrice() const {
    return price;
}

void Product::setName(string newName) {
    name = newName;
}

void Product::setPrice(double newPrice) {
    price = newPrice;
}

void Product::print() const {
    cout <<  name << " @ " << price << endl;
}

// Function that returns an object
Product makeProduct();

// For testing class Product
int main() {
    vector<Product> store;
    char repeat = 'Y';
    while ('Y' == repeat || 'y' == repeat) {
        cout << "Enter product data:\n";
        Product temp = makeProduct();
        store.push_back(temp);
        cout << "You entered:"
             << "\n   Name: " << temp.getName()
             << "\n   Price: " << temp.getPrice()
             << endl;

        cout << "Enter another product? (y/n): ";
        cin >> repeat;
    }

    cout << "\nAll your products:\n";
    for (unsigned i = 0; i < store.size(); i++) {
        store[i].print();
    }

    return 0;
}

Product makeProduct() {
    string name;
    cout << "Product name: ";
    cin >> name;
    double price;
    cout << "Price for a " << name << ": ";
    cin >> price;
    Product newProd(name, price);
    return newProd;
}

Check Yourself

  1. True or false: objects can be returned from functions.
  2. True or false: objects are always returned from functions by reference.
  3. True or false: the only way to return an object from a function is by using a return statement.

^ top

Passing Objects to Functions

  • Class types can be function parameters and we may pass objects to functions
  • We may pass objects by value or by reference
  • However, usually we pass objects by reference because it requires less work for the computer
  • As an example, let us write a function to compare the price of two products
  • One way we can write the function is as a non-member function
  • For example:
    bool isHigherPrice(Product& prod1, Product& prod2) {
        if (prod1.getPrice() > prod2.getPrice()) {
            return true;
        }
        return false;
    }
    
  • To call the function we use two explicit parameters like:
    if (isHigherPrice(prod1, prod2)) {
        cout << prod1.getName() << " costs more\n";
    } else {
        cout << prod2.getName() << " costs more\n";
    }
    
  • Create a new file productappNew.cpp file to add this function as follows: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <iostream>
using namespace std;

class Product {
public:
    Product();
    Product(string newName, double newPrice);
    string getName() const;
    double getPrice() const;
    void setName(string newName);
    void setPrice(double newPrice);
    void print() const;
private:
    string name;
    double price;
};

Product::Product() {
    name = "none";
    price = 0.0;
}

Product::Product(string newName, double newPrice) {
    setName(newName);
    setPrice(newPrice);
}

string Product::getName() const {
    return name;
}

double Product::getPrice() const {
    return price;
}

void Product::setName(string newName) {
    name = newName;
}

void Product::setPrice(double newPrice) {
    price = newPrice;
}

void Product::print() const {
    cout <<  name << " @ " << price << endl;
}

// Function with Product parameters
bool isHigherPrice(Product& prod1, Product& prod2);

// Function that returns an object
Product makeProduct();

// For testing class Product
int main() {
    cout << "Enter the first product:\n";
    Product prod1 = makeProduct();
    cout << "Enter the second product:\n";
    Product prod2 = makeProduct();
    if (isHigherPrice(prod1, prod2)) {
        cout << prod1.getName() << " costs more\n";
    } else {
        cout << prod2.getName() << " costs more\n";
    }

    return 0;
}

bool isHigherPrice(Product& prod1, Product& prod2) {
    if (prod1.getPrice() > prod2.getPrice()) {
        return true;
    }
    return false;
}

Product makeProduct() {
    string name;
    cout << "Product name: ";
    cin >> name;
    double price;
    cout << "Price for a "
         << name << ": ";
    cin >> price;
    Product newProd(name, price);
    return newProd;
}

Check Yourself

  1. True or false: objects are usually passed by reference to improve execution speed.
  2. Of the following functions called from main(), ________ is made to a non-member function.
    1. Product bread;
    2. bread.read();
    3. bread.print();
    4. list(products);
  3. True or false: calling a member function, outside of a class, requires an object.

14A Exercise 1:  Add a non member function that returns an object and takes an object as a parameter.

1.  Start with the productAppNew.cpp code above. Save it as productAppNewer.cpp
2.  Add a new non member function:  mysteryProduct
3.  mysteryProduct will take a Product as a parameter and return a new Product.  Inside mysteryProduct,  it will copy the cost from the Product parameter and create a new product with a name of mystery and the cost from the Product parameter. It does not make any changes to the parameter Product.
4.  the prototype for mysteryProduct is:     Product mysteryProduct(Product& myProduct);
5.  call your new function mysteryProduct from within main(),  just before the return 0; statement.
6.  print your newly copied Product that is returned from your function.  It should print "mystery" as the name and the price from your original Product parameter.

^ top

Comparing Member Functions with Non-member Functions

  • We looked at comparing two Product objects using a non-member function:
    bool isHigherPrice(Product& prod1, Product& prod2) {
        if (prod1.getPrice() > prod2.getPrice()) {
            return true;
        }
        return false;
    }
    
  • Another way is to write the comparison as a member function:
    bool Product::isHigherPrice(Product& prod2) const {
        if (getPrice() > prod2.getPrice()) {
            return true;
        }
        return false;
    }
    
  • Note the difference in the parameter lists
  • To call the non-member function we use two explicit parameters:
    if (isHigherPrice(prod1, prod2)) {
        cout << prod1.getName() << " costs more\n";
    } else {
        cout << prod2.getName() << " costs more\n";
    }
    
  • To call the member function we use dot notation and one explicit parameter:
    if (prod1.isHigherPrice(prod2)) {
        cout << prod1.getName() << " costs more\n";
    } else {
        cout << prod2.getName() << " costs more\n";
    }
    
  • By using dot notation, the object name supplies an implicit parameter to the function
  • The implicit parameter identifies which object data to access for comparison against the object specified by the parameter
  • Note that we could have written the member function as:
    bool Product::isHigherPrice(Product& prod2) const {
        if (price > prod2.price) {
            return true;
        }
        return false;
    }
    
  • We do not need to use function calls because member functions can access private member variables directly

When to Write Member and NonMember Functions

  • Which solution is better: member or nonmember functions?
  • It depends on the ownership of the class
  • If you own the class, you should implement useful operations as member functions
  • If you are using a class supplied by someone else, you should write a nonmember function rather than changing the class
  • The author of the class may improve it and give you a new version
  • It would be a nuisance to have to add your modifications every time you received a new version of the class

Check Yourself

  1. True or false: member function tend to have fewer parameters than non-member functions.
  2. True or false: if a member function compares two objects, only one object parameter is needed.
  3. True or false: if you are the owner of a class, it is better to write a member function to implement useful behaviors.


14A Exercise 2:  Convert Class Product from Standard C++ to Arduino C++

Start with your code from 14A Exercise 1:  productAppNewer.cpp (use productAppNew.cpp if you don't have the newer file).

1.  In  your Arduino IDE,  create a new file called productApp.ino.  Then use the IDE tabs to create:  product.h and product.cpp 

2.  Copy the appropriate parts of the productAppNewer.cpp code to the correct file in the Arduino IDE.

3.  Correct the includes and convert to Setup() and Loop(),  convert string to String, etc,  put an underscore before the private variables, replace cin with Serial.readString() when appropriate

4.  You are done when your files compile and you can use the Serial Monitor to enter product names, etc.

5.  Turn in your 3 files:   productApp.ino, product.h and product.cpp to Canvas.


Comments