C++ Primer 学习笔记_23_函数(续二) -局部对象、内联函数、类的成员函数

C++ Primer 学习笔记_23_函数(续2) --局部对象、内联函数、类的成员函数

函数

--局部对象、内联函数、类的成员函数



一、局部对象

    在C++语言中,每个名字都有作用域,每个对象都有生命期;名字的作用域指的是该名字的程序文本区,对象的生命期是在程序的执行过程中对象的存在时间。



自动对象

1、只有当定义它的函数被调用时才存在的对象称为自动对象。自动对象在每次调用函数时创建和撤销

2、对于未初始化的内置类型的局部变量,其初值不确定。

3、形参也是自动对象,形参所占用的存储空间在调用函数时创建,而在函数结束时撤销。



静态局部对象

    一个变量如果位于函数的作用域内,但生命期跨越了这个函数的多次调用,这种变量往往很有用。则应该将这样的对象定义为static(静态的)

    static局部对象确保不迟于在程序执行流程第一次经过该对象的定义语句时进行初始化(比较难于理解o(∩∩)o...)。这种对象一旦被创建,程序结束前都不会撤销。当定义静态局部对象的函数结束时,静态局部对象不会撤销在该函数被多次调用的过程中,静态局部对象会持续存在并保持它的值

/*
*在第一次调用函数count_calls之前,cnt就已经创建并赋初值为0
*在执行函数 count_calls 时, 变量 ctr 就已经存在并且保留上次调用该函数时的值。 
*/
size_t count_calls()
{
	
    static size_t cnt = 0;
    return ++cnt;
}
int main()
{
    for (int i = 0; i != 10; ++i)
    {
        cout << count_calls() << endl;
    }
    return 0;
} 

//P221 习题7.27 用static实现阶乘
int factorial()
{
	static int j = 0;
	static int res = 1;
	++j;

	return (res *= j);
}

int main()
{
	for (int i = 0; i != 5; ++i)
	{
		cout << factorial() << endl;
	}
}

二、内联函数

使用“小操作”函数的好处:

    1)阅读和理解函数的调用,要比读一条用等价的表达式要容易得多。

    2)如果需要做任何修改,修改函数要比找出并修改每一处等价表达式容易得多。

    3)使用函数可以确保统一的行为,每个测试都保证以相同的方式实现。

    4)函数可以重用,不必为其他应用重写代码。

但是这样的函数也存在者一些缺陷,调用函数比求解等价表达式要慢得多。在大多数的机器上,调用函数都要做很多工作:

    1)调用前先保存寄存器,并且要在返回时恢复

    2)复制实参

    3)程序必须转向一个新的位置执行


1、使用内联函数避免函数调用的开销

   将函数指定为inline函数,就是将它在程序中每个调用点上“内联地”展开。从而消除了把表达式写成函数的额外执行开销。

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

   内联说明(inline)对于编译器来说只是一个建议,编译器可以选择忽略这个建议;一般来说,内联机制使用与优化小的,只有几行而且经常被调用的函数。


2、把内联函数写入头文件

   内联函数要在头文件中定义,这一点不同于其他函数!

   在头文件中加入或修改内联函数时,使用了该头文件的所有源文件都必须重新编译!因为修改此处的头文件相当于修改了各个源文件!

//P222 习题7.39
inline bool isShorter(const string &str1,const string &str2)
{
	return str1.size() < str2.size();
}

三、类的成员函数

class Sales_item
{
public:
	double avg_price() const;
	bool same_isbn(const Sales_item &rhs) const
	{
		return isbn == rhs.isbn;
	}

private:
	std::string isbn;
	unsigned int units_sold;
	double revenue;
};

    函数原型必须在类中定义,而函数体既可以在类中定义,也可以在类外定义,一般比较短小的函数定义在类的内部。

    编译器隐式的将在类内定义的成员函数当作内联函数。

    类的成员函数可以访问该类的private成员。


1、成员函数含有额外的、隐含的形参

    每个成员函数都有一个额外的、隐含的形参将该成员函数与调用该函数的类对象绑定在一起。而这个形参就是this指针!

    因此,语句

	total.sime_isbn(trans);

就如同编译器这样重写了这个函数调用:

	Sales_item::same_isbn(&total,trans);

2const成员函数的引入

    const改变了隐含的this形参的类型。在调用total.same_isbn(trans),隐含的this形参将是一个指向total对象的constSales_Item*类型的指针:

bool Sales_item::same_isbn(const Sales_item *const this,
                           const Sales_item &rhs) const
{
    return (this -> isbn == rhs.isbn);
}

使用这种方式的const函数称为常量成员函数。由于this是指向const对象的指针,const成员函数不能修改调用该函数的对象,因此,函数avg_price和函数same_isbn只能读取而不能修改调用他们的对象的数据成员。


3this指针的使用

由于this指针是隐式定义的,因此不需要在函数的形参表中包含this指针,实际上,这样做也是非法的。但是,在函数体中可以显式地使用this指针。如:

bool Sales_item::same_isbn(const Sales_item &rhs) const
{
	return (this -> isbn == rhs.isbn);
}
double Sales_item::avg_price() const
{
	if (units_sold)
		return (this -> revenue) / (this -> units_sold);
	else
		return 0;
}

在类外定义成员函数时,返回类型和参数表必须和函数声明一致,如果函数被声明为const成员函数,那么函数定义时形参表后面也必须有const


4、定义构造函数

Sales_item::Sales_item():units_sold(0),revenue(0){};

    1)通常构造函数会作为类的接口的一部分,因此必须将构造函数定义为public的。

    2)在冒号和花括号之间的代码称为构造函数的初始化列表。构造函数的初始化列表为类的一个或多个数据成员指定初值。它跟在构造函数的形参表之后,以冒号开头。构造函数的初始化式是一系列成员名,每个成员后面是括在圆括号中的初始值。多个成员的初始化用逗号分隔。

    3)如果没有为一个类显式定义任何构造函数,编译器将自动为这个类生成默认构造函数。由编译器创建的默认构造函数通常称为合成的默认构造函数。

对于具有类类型的成员,如isbn,则会调用该成员所属类自身的默认构造函数实现初始化。而内置类型成员的初值却要依赖于对象如何定义,如果对象定义为全局对象,或定义为静态局部对象,则将这些成员初始化为0,不然,则不提供这些成员的初始化工作,这些成员没有初始化!


【建议:】

    合成的默认构造函数一般适用于仅包含类类型成员的类。而对于含有内置类型或复合类型成员的类,则通常应该定义他们自己的默认构造函数初始化这些成员。


5、类代码文件的组织【摘抄】

    通常情况下,将类的声明放置在头文件中,在类外定义的成员函数放置在源文件中,C++程序员习惯使用一些简单的规则给头文件及其关联的类定义代码命名。类定义应置于名为type.htype.H的文件 中,type指在该文件中定义的类的名字成员函数的定义则一般存储在与类同名的源文件中。依照这些规则,我们将类Sales_item放在名为Sales_item.h的文件中定义。任何需使用这个类的程序,都必须包含这个头文件。而Sales_item的成员函数的定义则应该放在名为Sales_item.cc的文件中。这个文件同样也必须包含Sales_item.h头文件

//P227 习题7.31
class Sales_item
{
public:
    Sales_item():units_sold(0),revenue(0) {};
    double avg_price() const;
    bool same_isbn(const Sales_item &rhs) const;

    void scan();
    void print() const;

private:
    std::string isbn;
    unsigned int units_sold;
    double revenue;
};

bool Sales_item::same_isbn(const Sales_item &rhs) const
{
    return (this -> isbn == rhs.isbn);
}
double Sales_item::avg_price() const
{
    if (units_sold)
        return (this -> revenue) / (this -> units_sold);
    else
        return 0;
}

void Sales_item::scan()
{
	cout << "Please input the isbn、units_sold & price" << endl;
	double price;
	cin >> isbn >> units_sold >> price;
	revenue += units_sold * price;
}

void Sales_item::print() const
{
	cout << "ISBN: " << isbn << endl;
	cout << "Units_sold: " << units_sold << endl;
	cout << "Revenue: " << revenue << endl;
	cout << "Avg_price: " << avg_price() << endl;
}

int main()
{
//	freopen("input.txt","r",stdin);
	Sales_item item;
	item.scan();
	item.print();
}


2楼huazai857昨天 18:37
强!
Re: u012333003昨天 20:28
回复huazai857额呀,过奖了,这些都还挺基础的,只是现在写这东西好麻烦~都一起加油哈~
1楼leijferrari昨天 18:34
我去 表示hold不住,这些个都是笔试写?