《C++ Primer第五版》学习笔记(4)---expressions,statements,functions&classes

1     Expressions

Because there are no guarantees for how the sign bit is handled, we strongly recommend using unsigned types with the bitwise operators.

2     Statements

2.1    Null Statements

;// null statement(为啥不搞个null;之类的呢,导致下面这种容易出错的场景出来)

// disaster: extra semicolon: loop body is this null statement

while (iter != svec.end()) ; // the while body is the empty statement

++iter;  // increment is not part of the loop

Best Practices: Null statements should be commented. That way anyonereading the code can see that the statement was omitted intentionally.

 

2.2   Jump Statements

Jump statements interrupt the flow of execution. C++ offers four jumps:break, continue, and goto and return.

2.3   try Blocks&Exception handling

throw expressions, which the detecting part uses to indicate that it encountered something it can’t handle. We say that a throw raises an exception.

try blocks, which the handling part uses to deal with an exception. A try block startswith the keyword tryand ends with one or more catch clauses.

Exceptions thrown from code executed inside a try block are usually handled by one of the catch clauses. Because they “handle” the exception, catch clauses are also known as exception handlers.

A set of exception classes that are used to pass information about what happened between a throw and an associated catch.

In a more realistic program, the part that adds the objects might be separated from the part that manages the interaction with a user. In this case,we might rewrite the test to throw an exception rather than returning an error indicator:

If no appropriate catch is found, execution is transferred to a library function named terminate. The behavior of that function is system dependent but is guaranteed to stop further execution of the program.

3     Functions

3.1    function basics

function prototype:一个函数包括四个部分:一个返回类型、一个函数名、一个参数列表(parameter list)和函数体。前三个部分合在一起称为function prototype(函数原型),即function declaration,四个部分合在一起即function definition.

Parameters and Arguments: Arguments are the initializers for a function’s parameters.

3.2     Argument Passing

3.2.1       Passing Arguments by Value

When the argument value is copied, the parameter and argument are independent objects. We say such arguments are “passed by value” or alternatively that the function is “called by value.”

指针形参与其他非引用形参一样,对该指针的改变也是仅改变其局部拷贝,但是可以通过解引用,改变指针所指向的值。

// function that takes a pointer and setsthe pointed-to value to zero

void reset(int *ip)

{

*ip = 0;  // changes the value of the object to whichip points

ip = 0;  // changes only the local copy of ip; theargument is unchanged

}

Programmers accustomed to programming in Coften use pointer parameters C++ to access objects outside a function. In C++,programmers generally use reference parameters instead.

3.2.2       Passing Arguments by Reference

Reference parameters are often used to allow afunction to change the value of one or more of its arguments.

// function that takes a reference to an intand sets the given object to zero
void reset(int &i)  // i is justanother name for the object passed to reset
{
  i = 0; // changes the value of the object to which i refers
}

 

Using References to Avoid Copies. It can be inefficient to copyobjects of large class types or large containers. Moreover, some class types(including the IO types) cannot be copied. Functions must use referenceparameters to operate on objects of a type that cannot be copied.

Reference parameters that are not changedinside a function should be references to const(而且加上const还有更多的优势,如果参数不加const,则传入的实参只能是同类型的非const类型):

// compare the length of two strings
bool isShorter(const string &s1, const string &s2)
{
 return s1.size() < s2.size();
}

 

如果要改变指针,那么可以使用指向指针的引用,语法如下:int *&v1;这种定义从右至左理解:v1是一个引用,它与指向int型的对象相关联。

Vector和其他容器类型的形参,从避免复制的角度,应该传递引用,然而C++程序员更倾向于传递容器要处理的元素的迭代器,一般是开始和结束元素。

3.2.3    Array ReferenceParameters

数组形参与其他形参有两个不同:不能复制数组,使用数组名时自动转换为第一个元素的指针。数组形参的定义有三种方式:void printvalue(int *); void printValues(int []); int printValue(int[10]);第三种方式很容易引起误解,因为实际上编译器并不检查该长度,也不会限制传入的参数的长度。通过数组形参的任何改变都会改变数组本身,因此在不需要修改数组形参的元素时,应将形参定义为const。

通过引用传递数组时,写法为int(&attr)[10],这时长度是有意义的,限制实参必须为这个长度的数组。另外,这种写法必须加括号, int &attr[10]是一个引用数组,而不是对一个数组的引用。

// ok: parameter is a reference to an array;the dimension is part of the type
void print(int (&arr)[10])
{
for (auto elem : arr)
   cout << elem << endl;

}

3.2.4       Functions with Varying Parameters

Sometimes we do not know in advance how manyarguments we need to pass to a function. The new standard provides two primary ways to write a function thattakes a varying number of arguments: If all the arguments have the same type,we can pass a library type named initializer_list.If the argument types vary, we can write a special kind of function, known as avariadic template.

void error_msg(initializer_list<string>il)

{

for (auto beg = il.begin(); beg!= il.end(); ++beg)

cout << *beg << " " ;

cout << endl;

}

 

C++ also has a special parameter type,ellipsis, that can be used to pass a varying number of arguments. However, itis worth noting that this facility ordinarily should be used only in programsthat need to interface to C functions.

Ellipsis parameters are in C++ to allowprograms to interface to C code that uses a C library facility named varargs.Generally an ellipsis parameter should not be used for other purposes.

void foo(parm_list, ...);

void foo(...);

3.3     Return Types and the return Statement

3.3.1       Functions That Return a Value

1.Failing to provide a return after a loopthat contains a return is an error. However, many compilers will not detectsuch errors(太不人性化了,g++4.8.2也不检测,为毛啊):

bool str_subrange(const string &str1,const string &str2)
{
// same sizes: return normal equality test
if (str1.size() == str2.size())
return str1 == str2;  // ok: == returnsbool
// find the size of the smaller string; conditional operator, see § 4.7 (p.151)
auto size = (str1.size() < str2.size())? str1.size() : str2.size();
// look at each element up to the size of the smaller string
for (decltype(size) i = 0; i != size; ++i) {
   if (str1[i] != str2[i])
   return; // error #1: no return value;compiler should detect this error

}

// error #2: control might flowoff the end of the function without a return

// the compiler might not detectthis error

}

 

2.We can return a value or a reference or a point, but Never Return a Reference or Pointer to a Local Object. One good way to ensure that thereturn is safe is to ask: To what preexisting object is the reference referring?

For the same reasons that it is wrong toreturn a reference to a local object, it is also wrong to return a pointer to alocal object.

3. Whether a function call is an lvaluedepends on the return type of the function. Calls to functions that return references arelvalues; other return types yield rvalues.In particular, we can assign tothe result of a function that returns a reference to nonconst:

char&get_val(string &str,string::size_type ix)
{
  return str[ix]; // get_val assumes thegiven index is valid
}

int main()
{
   string s("a value");
   cout << s << endl;  // prints a value
   get_val(s, 0) = ‘A‘; // changes s[0]to A
   cout << s << endl;  // prints A value
   return 0;
}

If the return type is a reference to const,then (as usual) we may not assign to the result of the call:

shorterString("hi","bye")= "X"; // error: return value is const

 

4.List Initializing the ReturnValue:

Under the new standard, functions can returna braced list of values.

vector<string> process()

{
// . . .
// expected and actual are strings
if (expected.empty())
    return {};  // return an empty vector
else if (expected == actual)

return {"functionX", "okay"}; // returnlist-initialized vector

else

return {"functionX", expected, actual};

}

3.3.2       Returning a Pointer to an Array

Because we cannot copy an array,a function cannot return an array. However, a function can return a pointer ora reference to an array.

int (*func(int i))[10];

To understand this declaration,it can be helpful to think about it as follows:

? func(int) saysthat we can call func with an int argument.
? (*func(int)) says we candereference the result of that call.
? (*func(int))[10] says thatdereferencing the result of a call to func yields an array of size ten.
? int  (*func(int))[10] says the element type in thatarray is int.

Fortunately, there are ways tosimplify such declarations.

1.Themost straightforward way is to use a type alias

typedef int arrT[10];  // arrT is a synonym for the type array often ints
using arrtT = int[10]; // equivalent declaration of arrT; see § 2.5.1 (p. 68)
arrT* func(int i);  // func returns apointer to an array of ten ints

2. Under the new standard, another way tosimplify the declaration of func is by using a trailing return type. A trailingreturn type follows the parameter list and is preceded by ->. To signal thatthe return follows the parameter list, we use auto where the return type ordinarilyappears:

auto func(int i) -> int(*)[10];

Because the return type comes after theparameter list, it is easier to see that func returns a pointer and that thatpointer points to an array of ten ints.

3.As another alternative, if we know thearray(s) to which our function can return a pointer, we can use decltype to declare the return type. For example, thefollowing function returns a pointer to one of two arrays, depending on thevalue of its parameter:

int odd[] = {1,3,5,7,9};
int even[] = {0,2,4,6,8};
// returns a pointer to an array of five int elements
decltype(odd) *arrPtr(int i)
{
  return (i % 2) ? &odd : &even;// returns a pointer to the array
}

3.4     Overloaded functions

在C++中,出现在相同作用域的两个函数,如果函数名相同,但是参数列表不同,则为函数重载。

如果仅仅是返回值不同,则C++认为是非法的,因为在传统的C语言中每个函数在调用时,都可以不取其返回值。

functionstaking const and nonconst references or pointers have different parameters:
Record lookup(Account&);  // functionthat takes a reference to Account
Record lookup(const Account&); // new function that takes a const reference

Recordlookup(Account*);  // new function, takesa pointer to Account
Record lookup(const Account*); // new function, takes a pointer to const

Advice: Althoughoverloading lets us avoid having to invent (and remember) names for commonoperations, we should only overload operations that actually do similar things.There are some cases where providing different function names adds informationthat makes the program easier to understand.

 

thecompiler will prefer the nonconst versions when we pass a nonconst object orpointer to nonconst.

 

const_castsare most useful in the context of overloaded functions

//return a reference to the shorter of two strings

conststring &shorterString(const string &s1, const string &s2)
{
 return s1.size() <= s2.size() ? s1 :s2;
}

string &shorterString(string&s1, string &s2)
{
  auto &r =shorterString(const_cast<const string&>(s1),
  const_cast<conststring&>(s2));
  returnconst_cast<string&>(r);

}

重载有作用域问题,如果在局部又声明了一个与外层作用域同函数名的函数,那么函数将被覆盖而不是被重载,即外层所有定义的函数都将失效,因为In C++, name lookup happensbefore type checking.  Bad practice:usually it‘s a bad idea to declare functions at local scope.

3.5    Features for Specialized Uses

3.5.1       Default Arguments

默认参数默认参数必须出现在参数列表的最后部分。

个人认为默认参数最好使的地方在于:如果需要对一个函数增加几个参数时,后面新增加的参数都设置为有默认参数,则使得前面已经写好的客户代码都不需要改动,这是非常有意义的。

3.5.2       inline &constexpr function

A functionspecified as inline(usually) is expanded “in line” at each call to AvoidFunction Call Overhead. The call would be expanded duringcompilation, the run-time overhead of making shorter String a function is thusremoved.

Theinline specification is only a request to the compiler. The compiler may chooseto ignore this request.

In general,the inlinemechanism is meant to optimize small, straight-line functions thatare called frequently. Many compilers will not inline a recursive function. A 75-line functionwill almost surely not be expanded inline.

A constexpr function is a function that can be used in a constant expression.

A constexprfunction is permitted to return a value that is not a constant(A constexprfunction is not required to return a constant expression):

//scale(arg) is a constant expression if arg is a constant expression

constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }

Thescale function will return a constant expression if its argument is a constant expressionbut not otherwise.

Putinline and constexpr Functions in Header Files: The compiler needs thedefinition, not just the declaration, in order to expand the code. inline andconstexpr functions normally are defined in headers.

3.5.3       Aids for Debugging

This approachuses two preprocessor facilities: assert and NDEBUG.

assertis a preprocessor macro(The assertmacro is defined in the cassertheader.):

assert(expr);

Theassert macro is often used to check for conditions that “cannot happen.” butshould not be used to substitute for runtime logic checks or error checkingthat the program should do.

 

TheNDEBUG Preprocessor Variable

The behaviorof assert depends on the status of a preprocessor variable named NDEBUG. IfNDEBUG is defined, assert does nothing. By default, NDEBUG is not defined, so,by default, assert performs a run-time check.

We can“turn off” debugging by providing a #define to define NDEBUG. Alternatively,most compilers provide a command-line option that lets us define preprocessorvariables:

$CC -DNDEBUG main.C

It canbe useful as an aid in getting a program debugged but should not be used to substitutefor runtime logic checks or error checking that the program should do.

In additionto using assert, we can write our own conditional debugging code using NDEBUG.If NDEBUG is not defined, the code between the #ifndef and the #endif isexecuted. If NDEBUG is defined, that code is ignored.

C++ compilerdefines a local static array of const charthat holds the name of the function:_ _func_ _

the preprocessordefines four other names that can be useful in debugging:

__FILE__     string literal containing the nameof the file

_ _LINE__    integer literal containing thecurrent line number

_ _TIME__   string literal containing the timethe file was compiled

__DATE__   string literal containing the datethe file was compiled

3.6    Pointers to Functions

3.6.1       declaration

A functionpointer is just that—a pointer that denotes a function rather than an object. Likeany other pointer, a function pointer points to a particular type. A function’stype is determined by its return type and the types of its parameters. The function’sname is not part of its type.

// compareslengths of two strings
bool lengthCompare(const string &, const string &);

has typebool(const string&, const string&). To declare a pointer that can pointat this function, we declare a pointer in place of the function name:

bool(*pf)(const string &, const string &); // uninitialized

Startingfrom the name we are declaring, we see that pfis preceded by a *, so pf is apointer. To the right is a parameter list, which means that pfpoints to afunction. Looking left, we find that the type the function returns is bool.Thus, pfpoints to a function that has two const string&parameters andreturns bool.

The parenthesesaround *pfare necessary. If we omit the parentheses, then we declare pf as afunction that returns a pointer to bool:

//declares a function named pf that returns a bool*
bool *pf(const string &, const string &);

3.6.2       using pointers to functions

pf=lengthCompare;  // pf now points to thefunction named lengthCompare
pf = &lengthCompare; // equivalent assignment: address-of operator isoptional(飞机,又见飞机)

bool b1= pf("hello", "goodbye"); // calls lengthCompare
bool b2 = (*pf)("hello", "goodbye"); // equivalent call
bool b3 = lengthCompare("hello", "goodbye"); // equivalentcall

3.6.3     Function Pointer Parameters

Just aswith arrays, we cannot define parameters of function type, but can have aparameter that is a pointer to function. As with arrays, we can write a parameterthat looks like a function type, but it will be treated as a pointer:

// thirdparameter is a function type and is automatically treated as a pointer tofunction
void useBigger(const string &s1, const string &s2,bool pf(const string&, const string &));

//equivalent declaration: explicitly define the parameter as a pointer tofunction
void useBigger(const string &s1, const string &s2, bool (*pf)(conststring &, const string &));

//automatically converts the function lengthCompare to a pointer to function
useBigger(s1, s2, lengthCompare);

别名:

// Funcand Func2 have function type

typedefbool Func(const string&, const string&);
typedef decltype(lengthCompare) Func2; // equivalent type

 

// FuncPand FuncP2 have pointer to function type

typedefbool(*FuncP)(const string&, const string&);
typedef decltype(lengthCompare) *FuncP2; // equivalent type

It isimportant to note that decltype returns the function type; the automaticconversion to pointer is not done.

Becausedecltypereturns a function type, if we want a pointer we must add the * ourselves.

We canredeclare useBigger using any of these types:

//equivalent declarations of useBigger using type aliases
void useBigger(const string&, const string&, Func);
void useBigger(const string&, const string&, FuncP2);

Both declarations declare the same function. In the first case, thecompiler will automatically convert the function type represented by Functo apointer.

3.6.4       Returninga Pointer to Function

we must write the return type as a pointer type;the compiler will notautomatically treat a function return type as the corresponding pointer type.

by far the easiest way to declare a function that returns a pointer tofunction is by using a type alias:

usingF = int(int*, int);  // F isa function type, not a pointer

using PF = int(*)(int*, int); // PF is a pointer type

PF  f1(int); // ok: PF is apointer to function; f1 returns a pointer to function
F  f1(int);  // error: F is a function type; f1 can‘treturn a function
F  *f1(int); // ok: explicitly specifythat the return type is a pointer to function

Of course, we can also declare f1directly, which we’d do as

int(*f1(int))(int*, int);

Reading this declaration from the inside out, we see that f1has aparameter list, so f1 is a function. f1 is preceded by a *so f1returns apointer. The type of that pointer itself has a parameter list, so the pointerpoints to a function. That function returns an int.

it’s worth noting that we can simplify declarations of functions thatreturn pointers to function by using a trailing return:
auto f1(int) -> int (*)(int*, int);

Using auto or decltype for Function Pointer Types

string::size_type sumLength(const string&, const string&);
string::size_type largerLength(const string&, const string&);
// depending on the value of its string parameter,
// getFcn returns a pointer to sumLength or to largerLength
decltype(sumLength) *getFcn(const string &);

local static objects:Local objects whose value persists across calls tothe function. Local static objects that are created and initialized beforecontrol reaches their use and are destroyed when the program ends.

4      Classes

The fundamental ideas behindclasses are data abstraction and encapsulation. Data abstraction is a programming (anddesign) technique that relies on the separation of interfaceand implementation. The interface of a classconsists of the operations that users of the class can execute. Theimplementation includes the class’ data members, the bodies of the functionsthat constitute the interface, and any functions needed to define the classthat are not intended for general use.

Encapsulationenforces the separation of a class’ interface and implementation. A class thatis encapsulated hidesits implementation—users of the class can use the interface but have noaccess to the implementation.

A classthat uses data abstraction and encapsulation defines an abstract data type. In an abstract data type, the class designerworries about how the class is implemented. Programmers who use the class neednot know how the type works.They can instead think abstractlyabout what thetype does.

4.1    Defining Abstract Data Types

4.1.1       User的概念

Programmerstend to think about the people who will run their applications as users.Similarlya class designer designs and implements a class for users of that class. Inthis case, the user is a programmer, not the ultimate user of the application.

Whenwe refer to a user, the context makes it clear which kind of user is meant. Ifwe speak of user code or the user of the Sales_dataclass, we mean a programmerwho is using a class.If we speak of the user of the bookstore application, we mean the manager ofthe store who is running the application.

C++ programmerstend to speak of users interchangeably as users of the application or users ofa class.

4.1.2     const Member Functions

std::stringisbn() const { return this->bookNo; }

Thekeyword const that follows the parameter list is to modify the type of theimplicit this pointer. A const followingthe parameter list indicates that this is a pointer to const. Member functionsthat use const in this way are const member functions.

The factthat this is a pointer to const meansthat const member functions cannot change the object on which they are called.Thus, isbn may read but not write to the data members of the objects on whichit is called.

Objects thatare const, and references or pointers to const objects, may call only const memberfunctions.

Defining a Member Function outsidethe Class:

doubleSales_data::avg_price() const {…}

4.1.3     Defining a Function to Return “This” Object

Sales_data& Sales_data::combine(const Sales_data &rhs)
{
    units_sold += rhs.units_sold; // addthe members of rhs into
    revenue += rhs.revenue;  // the members of ‘‘this‘‘ object
    return *this;// return the object on which the function was called
}

Toreturn an lvalue, our combinefunction must return a reference. Because the left-handoperand is a Sales_dataobject, the return type is Sales_data&.

4.1.4     Defining Nonmember Class-Related Functions

Althoughnonmember Class-Related Functions are conceptually part of the interface of theclass, they are not part of the class itself.Functions that are conceptuallypart of a class, but not defined inside the class, are typically declared (butnot defined) in the same header as the class itself.

istream&read(istream &is, Sales_data &item)
{
  double price = 0;
  is >> item.bookNo >>item.units_sold >> price;
  item.revenue = price * item.units_sold;
  return is;
}

The IOclasses are types that cannot be copied, so we may only pass them by reference.Moreover, reading or writing to a stream changes that stream, so both functionstake ordinary references, not references to const. The second thing to note isthat print does not print a newline. Ordinarily, functions that do outputshould do minimal formatting.

4.1.5     Constructors

The default constructor is one that takes no arguments. Thecompiler-generatedconstructor is known as the synthesized default constructor.

Synthesizeddefault constructor(有这个功能但是又有第二条风险,干脆取消算了):

1.The compilergenerates a default constructor automatically only if a class declares no constructors.

2.Membersof built-in type that are default initialized have undefined value,so Classes thathave members of built-in or compound type usually should rely on thesynthesized default constructor onlyif all such members have in-class initializers.

3.A thirdreason that some classes must define their own default constructor is that sometimesthe compiler is unable to synthesize one. For example, if a class has a memberthat has a class type, and that class doesn’t have a default constructor, then thecompiler can’t initialize that member.

Constructor Initializer List

Theconstructor initializer is a list of member names, each of which is followed bythat member’s initial value in parentheses (or inside curly braces). Multiplemember initializations are separated by commas.

Sales_data(conststd::string &s, unsigned n, double p):bookNo(s), units_sold(n),revenue(p*n) { }

4.2    Access Control andEncapsulation

4.2.1       access specifier

    access specifiers to enforce encapsulation:public,protected,private

它有两个重要意义:一是对于客户程序而言,它只需要关心public部分的定义;二是它使得编写该应用的人可以在提供应用程序接口后,随意更改其中的实现本身。这使得在设计类的时候,最主要的部分在于设计public部分,也就是与客户程序的接口部分。

Struct与class唯一的区别在于,struct默认的类成员和成员函数都是公共的,而class默认的都是私有的。

4.2.2       friend

A classcan allow another class or function to access its nonpublic members by makingthat class or function a friend.

class Sales_data{
// friend declarations for nonmember Sales_data operations added
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream &read(std::istream&, Sales_data&);
friend std::ostream &print(std::ostream&, const Sales_data&);
// other members and access specifiers as before
public:
   Sales_data() = default;
   Sales_data(const std::string &s,unsigned n, double p):bookNo(s), units_sold(n),        revenue(p*n) { }
   Sales_data(const std::string &s):bookNo(s) { }
   Sales_data(std::istream&);
   std::string isbn() const { returnbookNo; }
   Sales_data &combine(constSales_data&);
private:
  std::string bookNo;
  unsigned units_sold = 0;
  double revenue = 0.0;

};

//declarations for nonmember parts of the Sales_data interface

Sales_dataadd(const Sales_data&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);

 

Friendship between Classes

classScreen {

//Window_mgr memberscan access the private parts of class Screen

friendclass Window_mgr;

// ...rest of the Screen class

}

 

Making A Member Function a Friend

class Screen{

//Window_mgr::clear must have been declared before class Screen

friendvoid Window_mgr::clear(ScreenIndex);

// ...rest of the Screen class

};

4.3    Additional features

4.3.1       Mutable Data Members

Mutable Data Members: It sometimes(but not very often) happens that a class has a data member that we want to beable to modify, even inside a const member function. We indicate such membersby including the mutable keyword in their declaration.

Amutable data member is never const, even when it is a member of a const object.Accordingly, a const member function may change a mutable member.

As anexample, we’ll give Screena mutablemember named access_ctr, which we’ll use totrack how often each Screen member function is called:

classScreen{
public:
    void some_member() const;
private:
    mutable size_t access_ctr; // maychange even in a const object
   // other members as before

};

voidScreen::some_member() const
{
   ++access_ctr;  // keep a count of the calls to any memberfunction
   // whatever other work this memberneeds to do
}

access_ctrmember is a mutable member, so any member function, including const functions,can change its value.

4.3.2     Initializers for Data Members of Class Type

Underthe new standard, the best way to initiliazea member of class type is as an in-class initializer:

Class Window_mgr{
private:
// Screens this Window_mgr is tracking
// by default, a Window_mgr has one standard sized blank Screen
std::vector<Screen> screens{Screen(24, 80, ‘ ‘) };
};

A constmember function that returns *this as a reference should have a return typethat is a reference to const.

Everyclass defines a unique type. Two different classes define two different types evenif they define the same members.

4.3.3       delegating constructor

Adelegating constructor uses another constructor from its own class to performits initialization. It is said to “delegate” some (or all) of its work to thisother constructor.

classSales_data {
public:
    // nondelegating constructorinitializes members from corresponding arguments
    Sales_data(std::string s, unsignedcnt, double price):bookNo(s), units_sold(cnt), revenue(cnt*price) {}
    // remaining constructors alldelegate to another constructor
    Sales_data():Sales_data("", 0, 0) {}
    Sales_data(std::string s):Sales_data(s, 0,0) {}
    Sales_data(std::istream &is):Sales_data(){ read(is, *this); }
    // other members as before

};

In practice,it is almost always right to provide a default constructor if other constructorsare being defined

4.3.4       Implicit Class-Type Conversions

A constructorthat can be called with a single argument defines an implicit conversion fromthe constructor’s parameter type to the class type. Such constructors are sometimesreferred to as converting constructors.

string null_book= "9-999-99999-9";
// constructs a temporary Sales_data object

// withunits_sold and revenue equal to 0 and bookNo equal to null_book
item.combine(null_book);

Wecan prevent the use of a constructor in a context that requires an implicit conversionby declaring the constructor as explicit:

class Sales_data{
public:
    Sales_data() = default;
    Sales_data(const std::string &s,unsigned n, double p):bookNo(s), units_sold(n), revenue(p*n) { }
    explicitSales_data(const std::string &s): bookNo(s) { }
    explicitSales_data(std::istream&);
    // remaining members as before

};

When aconstructor is declared explicit, it can be used only with the direct form ofinitialization. We cannot use an explicit constructor with this form ofinitialization:

Sales_dataitem1(null_book);  // ok: directinitialization
// error: cannot use the copy form of initialization with an explicitconstructor
Sales_data item2 = null_book;

4.3.5       Literal Classes(没有搞懂啥用处,暴汗)

An aggregate class gives users direct access to itsmembers and has special initialization syntax:

For example,the following class is an aggregate:
structData {
   int ival;
   string s;
};

Dataval1 = { 0, "Anna" };

classes that are literal types may have function members that are constexpr. Such members must meet all the requirementsof a constexpr function.These memberfunctions are implicitly const.

Anaggregateclass (§ 7.5.5, p. 298) whose data members are all of literal type is a literalclass.

Anonaggregate class, that meets the following restrictions, is also a literal class:

?The data members all must haveliteral type.
?The class must have at least oneconstexprconstructor.
?If a data member has an in-classinitializer, the initializer for a member of builtin type must be a constantexpression, or if the member has class type, the initializer must use themember’s own constexpr constructor.
?The class must use defaultdefinition for its destructor, which is the member
that destroys objects of the class type.

Althoughconstructors can’t be const, constructors in a literal class can be constexpr functions.Indeed, a literal class must provide at least one constexpr constructor.

class Debug{
public:
   constexpr Debug(bool b = true): hw(b), io(b), other(b) {}
   constexpr Debug(bool h, bool i, bool o):hw(h), io(i), other(o) {}
   constexpr bool any() { return hw || io || other; }
   void set_io(bool b) { io = b; }
   void set_hw(bool b) { hw = b; }
   void set_other(bool b) { hw = b; }

private:
   bool hw;  // hardware errors other than IO errors
   bool io;  // IO errors
   bool other; // other errors

};

Aconstexpr constructor must initialize every data member. The initializers must eitheruse a constexprconstructor or be a constant expression.

constexprDebug io_sub(false, true, false);  //debugging IO
if (io_sub.any())  // equivalent toif(true)
   cerr << "print appropriateerror messages" << endl;
constexpr Debug prod(false); // no debugging during production
if (prod.any())  // equivalent toif(false)
   cerr << "print an errormessage" << endl;

4.4    static Class Members

As anexample, we’ll define a class to represent an account record at a bank:

class Account{
public:
   void calculate() { amount += amount *interestRate; }
   static double rate() { returninterestRate; }
   static void rate(double);

private:
   std::string owner;
   double amount;
   static double interestRate;
   static double initRate();

};

static memberfunctions are not bound to any object; they do not have a this pointer. As a result, static member functions may not bedeclared as const, and we may not refer to this in the body of a static member.This

We canaccess a static member directly through the scope operator:

doubler;
r = Account::rate(); // access a static member using the scope operator

Even thoughstaticmembers are not part of the objects of its class, we can use an object,reference, or pointer of the class type to access a staticmember.Member functionscan use staticmembers directly, without the scope operator. (多此一举,徒增复杂性,全都要求scope operator就结了)

Because static data members are not part of individual objects of theclass type,they are not defined when we create objects of the class. Asa result, they are not initialized by the class’ constructors. Moreover, ingeneral, we may not initialize a static member inside the class. Instead, we must define and initialize each static data member outsidethe class body. Like any other object, a static data member may bedefined only once.

Likeglobal objects staticdata members are defined outside any function. Hence, oncethey are defined, they continue to exist until the program completes.

//define and initialize a static class member
double Account::interestRate = initRate();

The bestway to ensure that the object is defined exactly once is to put the definitionof staticdata members in the same file that contains the definitions of theclass noninline member functions.

static Members Can Be Used in WaysOrdinary Members Can’t

1.astatic data member can have incomplete type. In particular,a static data member can have the same type as the class type of which it is amember. A nonstatic data member is restricted tobeing declared as a pointer or a reference to an object of its class:

class Bar{
  public:
     // ...
private:
     static Bar mem1; // ok: staticmember can have incomplete type
     Bar  *mem2; // ok: pointer member can have incomplete type
     Bar  mem3; // error: data members must have complete type

};

2. Anotherdifference between static and ordinary members is that we can use a static memberas a default argument:

class Screen{
public:
    // bkground refers to the staticmember
    // declared later in the classdefinition
    Screen& clear(char = bkground);

private:
    static const char bkground;
};

A nonstatic data member may not be used as a default argument because its value is part of the object of which it is a member.

《C++ Primer第五版》学习笔记(4)---expressions,statements,functions&classes

上一篇:再学C++ Primer(12)- C++中的高级内存管理


下一篇:微信公众平台消息体签名及加解密实例(Java)