Constructor initialization with arguments - c++

Another question from reading "Accelerated C++" by Andrew Koenig and Barbara E. Moo, and I'm at the chapter about constructors (5.1), using the example as before.
They write
we'll want to define two constructors: the first constructor takes no arguments and creates an empty Student_info object; the second takes a reference to an input stream and initializes the object by reading a student record from that stream.
leading to the example of using Student_info::Student_info(istream& is) {read(is);} as the second constructor which
delegates the real work to the read function. [...] read immediately gives these variables new values.
The Student_info class is
class Student_info {
public:
std::string name() const (return n;}
bool valid() const {return !homework.empty();}
std::istream& read(std::istream&);
double grade() const;
private:
std::string n;
double midterm, final;
std::vector<double> homework;
};
Since read is already defined as a function under the Student_info class, why is there a need to use a second constructor - isn't this double work? Why not just use the default constructor, and then the function since both are already defined?

It isn't double work on the contrary it simplifies the object initialization for the caller who instantiates the class
If you create the class with a single constructor every time you have to do
std::istream is = std::cin;
Student_info si();
si.read(is);
// si.foo();
// si.bar();
// si.baz();
Maybe some other operations can be added that can be done in constructor. So you don't have to write them again when you need to instantiate the class. If you create 10 instances you will have to write
( 10 -1 = ) 9 more lines and this isn't a good approach for OOP
Student_info::Student_info(istream& is)
{
read(is);
//foo();
//bar();
//baz();
}
But when you define two constructors like above, you can use the class like
std::istream is = std::cin;
Student_info si(is);
One of the main goals of OOP is writing reusable and not self repeating code and another goal is seperation of concerns. In many cases person who instantiates the object doesn't need to know implementation details of the class.
On your example read function can be private when its called inside constructor. This reaches us another concept of OOP Encapsulation
Finally this isn't double work and its a good approach for software design

My question is since read is already defined as a function under the Student_info class, why is there a need to use a second constructor - isn't this double work?
The second constructor takes an argument from the caller and passes it on to the read function. This allows you to directly instantiate a Student_info with an std::istream.
std::istream is = ....;
Student_info si(is); // internally calls read(is)
as opposed to
std::istream is = ....;
Student_info si;
si.read(is); // how could we use is if this call isn't made? Now we have to read some docs...
Why not just use the default constructor, and then the function since both are already defined?
Because it is better to construct objects such that they are in a coherent, useful state, rather than construct them first and then initialize them. This means users of the objects do not have to worry about whether the thing can be used or must first be initialized. For example, this function takes a reference to a Student_info:
void foo(const Student_into& si)
{
// we want to use si in a way that might require that it has been
// initialized with an istream
si.doSomethingInvolvingInputStream(); // Wait, what if the stream hasn't been read in?
// Now we need a means to check that!
}
Ideally, foo should not have to worry about the object has been "initialized" or made valid.

Related

Initialising const class data member from std::istream in constructor

I would like to initialise my class const member in the constructor from std::istream:
class MyClass {
private: const int dataMember;
public: MyClass(std::istream& is) { /* read into datamember }
}
I want to use >> operator of istream instance to fill in my dataMember but my dataMember is const. Is there any way in C++ to do that?
Sure, just wrap it in a function:
MyClass(std::istream& is) : dataMember{readInt(is)}{}
You can handle input errors by throwing from your helper function.
If you really want to do it without a helper function, you can force it with an istream_iterator:
MyClass(std::istream& is) : dataMember{*std::istream_iterator<int>(is)}{}
However, this will lead to somewhat wacky error handling. The error handling can be done via std::istream::exceptions, but the caller would have to remember to enable them. Otherwise, a failing read operation will invoke UB.
I prefer the helper function for that error handling reason. (Especially as it took me three iterations to get it right and I advocated UB in one.)
You can create a function that takes in a stream and returns an object. This function extracts the data from the stream and puts it in the constructor.
static MyClass fromIStream(std::istream& is) {
int datamember;
is >> datamember;
return MyClass(datamember);
}

passing class objects in C++

I'm taking a C++ course and I'm stuck on classes and objects. I'm working on an assignment that, in a nutshell, creates a class that takes two variables (let's say length and width).
I've figured out how to do this using get and set functions. But then, we have to use math on these variables. We're supposed to write a function that takes one instance of the class as a parameter (so two variables), and then does math on both this object (the one taken as a parameter) and object that the method of was called.
Part of why I'm confused is the language, and I'm not sure exactly what that means. So far, like I said, I managed to be able to end up with setLength and setWidth variables set via user input. I am really, really stuck on trying to then pass these values (or this object?) to a function and then call the method of another object?
Maybe if someone could help me figure out what "taking an object as a parameter and then doing math on the object i called the method of" means? Or just help with passing objects?
Passing an object works just like passing other kinds of variables. If you were passing an integer into a function, you'd use this syntax for declaring the function:
void myFunction(int myInt);
and if you were passing in an object of class Foo, it would be:
void myOtherFunction(Foo myFoo);
This is sort of like saying, "This the thing I want you to use in your calculations. Copy the object I pass in here!. Passing by reference instead:
void myFunction(int &myInt);
void myOtherFunction(Foo &myFoo);
lets you modify the value you pass. It's also significantly cheaper with larger objects than passing by value which was the original syntax in this answer. You can think of it as you saying to the computer, "I know you want this value, but I'm not going to let you copy this. Just look over there, instead, and you'll find what I want you to work with." But sometimes you don't want to modify the thing you're working with!
Sure, you could be very careful in your function to avoid changing things, but the C++ language lets you say that you shouldn't modify the variable, and then will check that you don't modify it for you!
This can be accomplished by using
void yetAnotherFunction(const Foo &myFoo);
The const is what says "Don't let me be modified!" to the compiler, and the compiler will listen.
Say you want to assign a few values to a simple object, using a (non-member) function:
// a struct should usually hold simple groups of data,
// that don't do much by themselves. Their members are
// also public by default.
struct MySimpleType{
int first;
int second;
};
// object is passed by reference so it can be modified.
void modifier(MySimpleType &object, int newFirst, int newSecond){
object.first = newFirst;
object.second = newSecond;
}
then in your client code (probably a main function, at this point in your coding career) you do this:
MySimpleType object;
modifier(object, 13, 12);
cout << object.first << ", " << object.second;
which should print out:
13, 12
Thinking of pieces of code as "objects" can be difficult a first, but it will likely be one of the most important things you learn (because object oriented programming is widely used in industry and academia). There is quite a lot of background you need in order to use objects effectively in c++, but I'll try give a concise introduction..
Firstly, it's important that you understand the difference between a "class" and an "object." A class is an abstraction that allows you to define an object. If I want to make a Horse object, I use a Horse class to define what is important about a horse. This class might have fields defining its name, owner, hair color etc. However, the Horse class is not a horse. It tells you what it means to be a Horse, but it isn't one. In order to define an "object" of type Horse, we would write the following:
Horse myHorse = new Horse("Sea Biscuit", "Howard", "Black");
Keep in mind that Horse is the class, but Sea Biscuit is the horse itself (the object).
You may be well aware of the above, but it can often be a tough concept to grasp, so I thought I would mention it.
Now, if you want to perform math on some objects, this is relatively straightforward with using member functions. Lets define a new class to do some math on (because horses and math don't mix).
class Wallet
{
int _pennies;
// This is a constructor. It allows us to write: Wallet myWallet(100);
public Wallet(int pennies)
{
_pennies = pennies;
}
public void addPennies(int pennies)
{
_pennies = _pennies + pennies;
}
public void stealPennies(Wallet &stolenWallet)
{
int stolenPennies = stolenWallet._pennies;
stolenWallet._pennies = 0;
this.addPennies(stolenPennies);
}
}
We can now make some objects, and modify the fields in both objects with a single call to stealPennies:
int main()
{
Wallet myWallet(10); // Creates a wallet with 10 cents.
Wallet anotherWallet(50); // Creates another wallet with 50 cents.
myWallet.stealPennies(anotherWallet);
// myWallet now has 60 cents, and anotherWallet has none.
}
Note: The & before the name of the argument in the stealPennies function means it will be passed by reference. Usually when you pass an argument to a function it is passed by value, which means the variable in the function is a copy of the argument you passed. Putting the & before the name of the argument means the variable in the function is the same variable instead of a copy. (This is highly simplified.. it's unlikely that you will be able to fully understand passing by reference until you become familiar with pointers). Also, it is common practice to use some kind of naming convention when defining variables that are part of a class. Putting an underscore before the variable name is common (such as _pennies in this example).
Hopefully this is helpful to you (and hopefully it works, as I can't test it at the moment). I have tried to make the code as readable and explicit as possible.
As from your comment:
" I can't seem to figure out how to "assign" this user input to the object. So in the example above, i have setLength variables taken from user input. I cant figure out how to assign these variables to a new object, so that then the object is passes, the user input ( in the form of variables) is passed along with it!"
What I think you actually need is some function(s) to manipulate your class member variables by reading from a std::istream, and passing the object instance targeted as a reference:
class foo {
public:
foo() : x(12), y(42.0) {}
private:
friend std::istream& operator>>(std::istream& is, foo& subject);
std::istream& getfrominstream (std::istream& is) {
is >> x;
is >> y;
return is;
}
int x;
double y;
};
std::istream& operator>>(std::istream& is, foo& subject) {
return subject.getfrominstream(is);
}
Call like:
int main() {
foo f;
std::cin >> f;
}

C++ Default constructor not available

I'm currently learning C++ and reading through "C++ Primer 5th Edition". I just started learning about constructors and I'm having a bit of a problem that I can't figure out.
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data
{
//default constructor
Sales_data(const std::string &s, unsigned n, double p):
bookNo(s), units_sold(n), revenue(p*n) { }
//new members: operations on Sales_data objects
std::string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
double avg_price() const;
//data members
std::string bookNo;
unsigned units_sold;
double revenue;
};
I'm pretty sure that the default constructor I wrote is correct (considering it's the one written in the book), but obviously I'm missing something here. I don't see any syntax errors or anything and all of the built-in members are being initialized so I have no idea what's wrong.
EDIT :
I just found out that it's not my header file giving the error, it's actually my source file. When I create a Sales_data object like:
Sales_data total;
it gives me the "No appropriate default constructor available" error. I'm still unsure as to what's wrong considering the author gave three ways to write a default constructor, these are them:
struct Sales_data {
// constructors added
Sales_data() = default; //Number 1
Sales_data(const std::string &s): bookNo(s) { } //Number 2
Sales_data(const std::string &s, unsigned n, double p): //Number 3
bookNo(s), units_sold(n), revenue(p*n) { }
If those aren't default constructors, then what exactly are they/is their purpose?
A default constructor is a constructor that can be called without passing any argument.
This means that it must take no parameters or that all of them must have a default value.
The default constructor is needed for example when you write
MyClass x;
because the compiler must be able to generate code to build such an object without any arguments.
Also standard containers may require a default constructor depending on how you use them: for example if you use std::vector::resize the library can be asked to increase the size of a vector containing your class instances, thus it must be able to create elements without providing any argument.
The problem isn't related to default constructors. The problem indeed is in Sales_data total;. What's the book number, sales price and number sold for total? You must provide them when constructing total.
The constructor of your class takes three parameters, those must be provided when you try to construct an object. Your current variable declaration doesn't provide any parameters:
Sales_data total;
Because there are no parameters provided, the compiler tries to use a constructor that doesn't take any parameters. Such a constructor is also called "default constructor". Your class doesn't have a constructor without parameters, so this doesn't work.
To use the existing constructor, you have to provide the parameters when you create the object:
Sales_data total("books", 28, 15.99);
Alternatively you could add a constructor to Sales_data that doesn't take any parameters (a default constructor), and initializes the class with some default values.

Creating a copy constructor with arguements for a class

I have created a simple class that takes an argument to the constructor as shown below.
class Jam
{
int age;
std::string name;
Jam *jam;
Jam(std::string argName) {
name = argName;
}
};
It takes the argument and initializes the Jam's name to the parameter passed. The only problem is that I'd like to be able to pass another copy of Jam to the constructor so I can initialize its pointer to an existing class by copying the values. Normally in C++ you could specify Jam *jam = new Jam(existingJam); and it would be copied by default, but since I already have std::string argName as a parameter, it refuses to let me do this.
I read an article here that explains how to create your own copy constructor, but it is rather tedious and involves copying each class member individually which seems like it would not make much sense for a class with 10+ data members. Is there a better way to do this than initializing each member individually?
Jam::Jam(std::string argName, Jam *argJam)
{
age = argJam->age;
//etc...
}
[It's quite possible I don't understand what you're asking, but here goes...]
The compiler provides a copy constructor if you don't write one yourself. Its behaviour is to copy each member variable in turn.

Classes in C++ with Ctor [duplicate]

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
The Definitive C++ Book Guide and List
i have a lot of questions about declaration and implementation, according to most (books, tutorials, blog entries) a class declaration with constructor, methods and member functions:
class Book
{
public:
Book(const string & author_,
const string & title_,
const string & publisher_,
double price_,
double weight_);
string getName()
{
string name;
name = author + ": " + title;
return name.substr(0, 40);
}
double getPrice();
double getWeight();
private:
string author, title, publisher;
double price, weight;
};
i understand all the the access level, the Constructor, reference operator (pointer too!), the pointer operator, but when I read things less trivial like:
class Type
{
public:
enum TypeT {stringT, intT, doubleT, unknownT};
// 1. which means "explicit"?
// 2. what's ": typeId(typeId_)"? after the Ctor declaration
explicit Type(TypeT typeId_) : typeId(typeId_) {}
// 3. "const" after the declaration which means?
BaseValue * newValue() const
{
return prototypes[typeId]->clone();
}
TypeT getType() const
{
return typeId;
}
static void init();
{
prototypes[stringT] = new Value<string>("");
prototypes[intT] = new Value<int>(0);
prototypes[doubleT] = new Value<double>(0);
}
private:
TypeT typeId;
static vector<BaseValue *> prototypes;
};
I feel lost and really have not found clear information about the above points.
Addition to answering my question, if you know somewhere where they have these "tricks" of language
A good introductory book to C++ should answer your questions.
explicit means the constructor must be mentioned explicitely in the code, the type cannot be constructed automatically from another type when required.
Member initialization. The class member is not constructed with its default constructor, but rather the arguments given here.
The method can be called on a const object.
1) By default, c++ will assume that any constructor taking one argument of another type can be used as an "implicit conversion" from the arg's type to the constructor's type; in your example, this would allow you to pass a TypeT into any function expecting a Type, and the constructor would assume you want it to run the Type(TypeT) constructor before actually calling the function.
the explicit keyword prevents that from happening; it tells the compiler that you only want that constructor run when you specifically call it.
2) in between the prototype of the constructor and its body, you can (and, for the most part, should) provide an initializer list; When a constructor is run, the system initializes every parent, then every contained member before running the body of the constructor. The initializer list tells the compiler which constructor you want to be run on a given member variable; in the case of your example, you're running the copy constructor on typeId.
If you don't provide an entry in the initializer list for a given member, the system will simply run the default constructor on that member before entering the body of the original class. This means that, should you assign to the member within the body of your class constructor, you'll be writing to that member's memory twice. This is necessary sometimes, but for many cases is simply wasteful.
3) const at the end of a method prototype makes a guarantee to the compiler that that method will not modify any of the member variables within the class instance when it is called. This allows the method to be called on const instances of your class, and, just like placing const on any variables that will remain unchanged, should be done whenever possible to guarrantee correctness and type safety.
As for which books to read, the link in your question's comments would be a good place to start. Since you seem to understand the barebones basics of the language, I would recommend starting with "Effective C++". It presents a list of best practices for C++, and is aimed at someone who understands the C aspects of the language.