2010-08-31
1、在其所在作用域有着明确含义的枚举常量,有着不占用空间,也没有任何运行期成本的巨大优点。字面常量是没有地址的,如 int* p = &100;是错误的语法。
2.全局变量阻碍代码重用,还是代码变得难以维护,全局变量增加了模块之间的耦合。全局变量经常要求(延迟到)运行期(才进行的)静3态初始化。
3、函数重载主要用于一组抽象意义相同,但实现不同的饿函数。而形式参数默认值主要处于简化, 为函数提供更简洁的接口。函数重载和形式参数默认值是两个毫不相干的语言特征,他们处于不同的目的而设计的,行为也完全不同。
4、声明引用的引用、指向引用的指针或引用的数组都是不合法的。没有空引用,也没用类型为void的引用。
如果函数 返回值为引用类型,这就以为着可以对该函数的返回值重新赋值。
指向数组的引用保留了数组的尺寸信息,而指针则不保留。
int arr[10];
int* parr = arr;//指向arr的第一个元素
int (&rarr)[10] = arr;//指向整个数组arr的引用
int arr2[3][4];
int (*parr2)[4] = arr2;//指向数组arr2的第一个元素
int (&rarr2)[3][4] = arr2;//指向数组arr2
printf("sizeof(parr)=%d sizeof(rarr)=%d/n", sizeof(parr), sizeof(rarr));
printf("sizeof(parr2)=%d sizeof(rarr2)=%d/n", sizeof(parr2), sizeof(rarr2));
答案自己想!
5、对索引运算符要有深刻的认识!p[3],p[-2],(-2)p[](注意-2要括起来,因为[]的运算级别高)等。
2010-09-02
自增与自减:
(1)、前置式总是优于后置式的。
(2)、前置应该返回一个可修改的左值,以模拟内建运算符的行为。以成效论,这就要求该运算符返回指向对象自身的引用。
(3)、后置形式的运算符一般利用前置式版本来实现。以成效论,后置运算符必需以传值的方式返回结果。前置总是优于后置的。
左值与右值
左值与右值的概念要追溯到 C 语言,由 C++ 语言继承了上来。C++ 03 3.10/1 如是说:“Every expression is either an lvalue or an rvalue.”左值与右值是指表达式的属性,而非对像的属性。
左值具名,对应指定内存域,可访问;右值不具名,不对应内存域,不可访问。临时对像是右值。左值可处于等号左边,右值只能放在等号右边。区分表达式的左右值属性有一个简便方法:若可对表达式用 & 符取址,则为左值,否则为右值。
注意区分 ++x 与 x++。前者是左值表达式,后者是右值表达式。前者修改自身值,并返回自身;后者先创建一个临时对像,并用 x 的值赋之,后将修改 x 的值,最后返回临时对像。
函数的返回值一般情况下是右值,C++ 03 5.2.2/10 如是说:“A function call is an lvalue if and only if the result type is a reference.”比如有 vector v 对像,则 v[0] 即为左值,因为 vector 容器的 [] 算符重载函数的返回值为引用。
左值与右值均可以声明为 const 和 non-const。
指向类成员函数的函数指针
class MyClass { public: MyClass(){} MyClass(int a):m_a(a){} void print() { cout<* (my->*p_print)();//通过函数指针调用print()函数 cout<*p_m_a<
注意typedef的用法
int main() { typedef int Array[10]; Array a; int i; for (i=0; i<10; i++) { a[i] = i; cout<
2010-09-03
1、数组名本身不占用任何存储空间的,它是没有地址的。数组是有地址的,这个地址是通过是数组名来指定的,但数组名自身并无地址。其实数组名就是改数组的地址。
2、多维数组的首元素的类型是个数组,而不是其基本数据类型。
int arr[2][3];
arr的类型为int (*)[3],该类型可以换为void*的隐式类型转换。
void *p;
int arr[2][3];p = arr;
3、编译器对于用户自定义的(隐式)类型转换仅作一次的尝试。
4、函数对象
尽管函数指针被广泛用于实现函数回调,但C++还提供了一个重要的实现回调函数的方法,那就是函数对象。函数对象(也称“算符”)是重载了“()”操作符的普通类对象。因此从语法上讲,函数对象与普通的函数行为类似。
用函数对象代替函数指针有几个优点,首先,因为对象可以在内部修改而不用改动外部接口,因此设计更灵活,更富有弹性。函数对象也具备有存储先前调用结果的数据成员。在使用普通函数时需要将先前调用的结果存储在全程或者本地静态变量中,但是全程或者本地静态变量有某些我们不愿意看到的缺陷。
其次,在函数对象中编译器能实现内联调用,从而更进一步增强了性能。这在函数指针中几乎是不可能实现的。
下面举例说明如何定义和使用函数对象。首先,声明一个普通的类并重载“()”操作符:
class Negate
{
public:
int operator() (int n) { return -n;}
};
重载操作语句中,记住第一个圆括弧总是空的,因为它代表重载的操作符名;第二个圆括弧是参数列表。一般在重载操作符时,参数数量是固定的,而重载“()”操作符时有所不同,它可以有任意多个参数。
因为在Negate中内建的操作是一元的(只有一个操作数),重载的“()”操作符也只有一个参数。返回类型与参数类型相同-本例中为int。函数返回与参数符号相反的整数。
使用函数对象
我们现在定义一个叫Callback()的函数来测试函数对象。Callback()有两个参数:一个为int一个是对类Negate的引用。Callback()将函数对象neg作为一个普通的函数名:
#include
using std::cout;
void Callback(int n, Negate & neg)
{
int val = neg(n); //调用重载的操作符“()”
cout << val;
}
不要的代码中,注意neg是对象,而不是函数。编译器将语句
int val = neg(n);
转化为
int val = neg.operator()(n);
通常,函数对象不定义构造函数和析构函数。因此,在创建和销毁过程中就不会发生任何问题。前面曾提到过,编译器能内联重载的操作符代码,所以就避免了与函数调用相关的运行时问题。
为了完成上面个例子,我们用主函数main()实现Callback()的参数传递:
int main()
{
Callback(5, Negate() ); //输出 -5
}
本例传递整数5和一个临时Negate对象到Callback(),然后程序输出-5。
模板函数对象
从上面的例子中可以看出,其数据类型被限制在int,而通用性是函数对象的优势之一,如何创建具有通用性的函数对象呢?方法是使用模板,也就是将重载的操作符“()”定义为类成员模板,以便函数对象适用于任何数据类型:如double,_int64或char:
class GenericNegate
{
public:
template T operator() (T t) const {return -t;}
};
int main()
{
GenericNegate negate;
cout<< negate(5.3333); // double
cout<< negate(10000000000i64); // __int64
}
如果用普通的回调函数实现上述的灵活性是相当困难的。
标准库中函数对象
C++标准库定义了几个有用的函数对象,它们可以被放到STL算法中。例如,sort()算法以
判断对象(predicate object)作为其第三个参数。判断对象是一个返回Boolean型结果的
模板化的函数对象。可以向sort()传递greater<>或者less<>来强行实现排序的升序或降序:
#include // for greater<> and less<>
#include //for sort()
#include
using namespace std;
int main()
{
vector vi;
//..填充向量
sort(vi.begin(), vi.end(), greater() );//降序( descending )
sort(vi.begin(), vi.end(), less() ); //升序 ( ascending )
}