Define read
's Sales_data parameter as plain reference since it's intended to change the revenue
's value.
Define print
's Sales_data parameter as a reference to const since it isn't intended to change any member's value of this object.
if(read(read(cin, data1), data2))
we can try to divide it like that:
std::istream &firstStep = read(cin, data1);
sdt::istream &secondStep = read(firstStep, data2);
if (secondStep)
the condition of the if
statement would read two Sales_data object at one time.
Sales_data() : units_sold(0) , revenue(0){}
There are no restrictions on how often an access specifier may appear.The specified access level remains in effect until the next access specifier or the end of the class body.
The members which are accessible to all parts of the program should define after a public specifier.
The members which are accessible to the member functions of the class but are not accessible to code that uses the class should define after a private specifier.
The only difference between using class
and using struct
to define a class is the default access level. (class
: private, struct
: public)
encapsulation is the separation of implementation from interface. It hides the implementation details of a type. (In C++, encapsulation is enforced by putting the implementation in the private part of a class)
Important advantages:
- User code cannot inadvertently corrupt the state of an encapsulation object.
- The implementation of an encapsulated class can change over time without requiring changes in user-level code.
public include: constructors, getName()
, getAddress()
.
private include: name
, address
.
the interface should be defined as public, the data shouldn't expose to outside of the class.
friend
is a mechanism by which a class grants access to its nonpublic members. They have the same rights as members.
Pros:
- the useful functions can refer to class members in the class scope without needing to explicitly prefix them with the class name.
- you can access all the nonpublic members conveniently.
- sometimes, more readable to the users of class.
Cons:
- lessens encapsulation and therefore maintainability.
- code verbosity, declarations inside the class, outside the class.
The class below can rely on it. It goes in Section 7.1.5:
..the synthesized versions are unlikely to work correctly for classes that allocate resources that reside outside the class objects themselves.
Moreover, the synthesized versions for copy, assignment, and destruction work correctly for classes that have vector or string members.
Hence the class below which used only built-in type and strings can rely on the default version of copy and assignment. (by @Mooophy)
The second call to display
couldn't print #
among the output, cause the call to set
would change the temporary copy, not myScreen.
#with '&'
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXX#XXXX
^^^
# without '&'
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
^^^
Pros
-
more explicit
-
less scope for misreading
-
can use the member function parameter which name is same as the member name.
void setAddr(const std::string &addr) {this->addr = addr;}
Cons
-
more to read
-
sometimes redundant
std::string getAddr() const { return this->addr; } // unnecessary
[clang]error: unknown type name 'pos'
fixed:
Screen::pos Screen::size() const
{
return height*width;
}
There is an error in
dummy_fcn(pos height)
^
Unknown type name 'pos'
typedef string Type;
Type initVal(); // use `string`
class Exercise {
public:
typedef double Type;
Type setVal(Type); // use `double`
Type initVal(); // use `double`
private:
int val;
};
Type Exercise::setVal(Type parm) { // first is `string`, second is `double`
val = parm + initVal(); // Exercise::initVal()
return val;
}
fixed
changed
Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
to
Exercise::Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
and Exercise::initVal()
should be defined.
In this case, the constructor initializer makes it appear as if
base
is initialized withi
and thenbase
is used to initializerem
. However,base
is initialized first. The effect of this initializer is to initializerem
with the undefined value ofbase
!
fixd
struct X {
X (int i, int j): base(i), rem(base % j) { }
int base, rem;
};
Sales_data first_item(cin); // use Sales_data(std::istream &is)
int main() {
Sales_data next; // use Sales_data(std::string s = "")
Sales_data last("9-999-99999-9"); // use Sales_data(std::string s = "")
}
Sales_data(std::istream &is = std::cin) { read(is, *this); }
illegal. cause the call of overloaded 'Sales_data()' is ambiguous.
Such as Book
class Book {
public:
Book() = default;
Book(unsigned no, std::string name, std::string author, std::string pubdate):no_(no), name_(name), author_(author), pubdate_(pubdate) { }
Book(std::istream &in) { in >> no_ >> name_ >> author_ >> pubdate_; }
private:
unsigned no_;
std::string name_;
std::string author_;
std::string pubdate_;
};
class Book {
public:
Book(unsigned no, std::string name, std::string author, std::string pubdate):no_(no), name_(name), author_(author), pubdate_(pubdate) { }
Book() : Book(0, "", "", "") { }
Book(std::istream &in) : Book() { in >> no_ >> name_ >> author_ >> pubdate_; }
private:
unsigned no_;
std::string name_;
std::string author_;
std::string pubdate_;
};
illegal, cause there are ten elements, each would be default initialized. But no default initializer for the temporary object.
No problem. cause C
have the default constructor.
- a) A class must provide at least one constructor. (unture, "The compiler-generated constructor is known as the synthesized default constructor.")
- b) A default constructor is a constructor with an empty parameter list. (unture, A default constructor is a constructor that is used if no initializer is supplied)
- c) If there are no meaningful default values for a class, the class should not provide a default constructor. (unture, the class should provide.)
- d) If a class does not define a default constructor, the compiler generates one that initializes each data member to the default value of its associated type. (unture, only if our class does not explicitly define any constructors, the compiler will implicitly define the default constructor for us.)
Whether the conversion of a string
to Sales_data
is desired depends on how we think our users will use the conversion. In this case, it might be okay. The string
in null_book probably represents a nonexistent ISBN.
Benefits:
- prevent the use of a constructor in a context that requires an implicit conversion
- we can define a constructor which is used only with the direct form of initialization
Drawbacks:
- meaningful only on constructors that can be called with a single argument
Both are noting happened.
(a) Sales_data &combine(Sales_data); // ok
(b) Sales_data &combine(Sales_data&); // [Error] no matching function for call to 'Sales_data::combine(std::string&)' (`std::string&` can not convert to `Sales_data` type.)
(c) Sales_data &combine(const Sales_data&) const; // [Error] assignment of member 'Sales_data::units_sold' in read-only object. (we cannot combine the other `Sales_data` object.)
Such as a function like that:
int getSize(const std::vector<int>&);
if vector has not defined its single-argument constructor as explicit. we can use the function like:
getSize(34);
What is this mean? It's very confused.
But the std::string
is different. In ordinary, we use std::string
to replace const char *
(the C language). so when we call a function like that:
void setYourName(std::string); // declaration.
setYourName("pezy"); // just fine.
it is very natural.
Sales_data
should have no in-class initializers.
FIXED:
struct Sales_data {
std::string bookNo;
unsigned units_sold;
double revenue;
};
shouldn't, cause a constexpr
function must contain exactly one return statement.
yes.
An aggregate class whose data members are all of literal type is a literal class.
What is a static class member?
A class member that is associated with the class, rather than with individual objects of the class type.
What are the advantages of static members?
each object can no need to store a common data. And if the data is changed, each object can use the new value.
How do they differ from ordinary members?
- a static data member can have incomplete type.
- we can use a static member as a default argument.
static double rate = 6.5;
^
rate should be a constant expression.
static vector<double> vec(vecSize);
^
we may not specify an in-class initializer inside parentheses.
Fixed:
// example.h
class Example {
public:
static constexpr double rate = 6.5;
static const int vecSize = 20;
static vector<double> vec;
};
// example.C
#include "example.h"
constexpr double Example::rate;
vector<double> Example::vec(Example::vecSize);