C++ Primer 笔记

来源:互联网 发布:php 10进制转36进制 编辑:程序博客网 时间:2024/05/18 00:16

Release版:与debug相比,有“返回值优化”它的目的就是为了省去临时对象的创建和销毁的巨大开销,这种开销对程序员不可见,但是会潜在对程序性能产生很大影响。

声明: 类型   , 名字。

定义: 分配存储空间 ,可以指定初始值。

程序中变量可以声明多次,只能定义一次。


隐含的this指针

成员函数具有一个附加的隐含形参,即指向该类对象的一个指针。这个隐含形参命名为 this,与调用成员函数的对象绑定在一起。


typedef :  定义类型的同义词。

eg:  typedef  double wages;  //typedef 名字可以用作类型说明符。

    wages hourly;     // double  hourly;

typeof,gcc中对c/c++语法的一个扩展,用来静态获取参数类型
比如
int a = 3;
typeof(a) b = 4; // 相当于 int b = 4;
typeof("12345") c = "abcde"; // 相当于 const char c[6] = "abcde"
vector<typeof(1.234)> a; // 相当于 vector<double> a;


用 class 和 struct 关键字定义类的唯一差别在于默认访问级别:

默认情况下,struct 的成员为 public,而 class 的成员为 private。


初始化vector

vector<T> v1;           vector 保存类型为 T 对象。 默认构造函数 v1 为空。
vector<T> v2(v1);    v2 是 v1 的一个副本。
vector<T> v3(n, i);   v3 包含 n 个值为 i 的元素。
vector<T> v4(n);      v4 含有值初始化的元素的 n 个副本。

初始化bitset 对象的方法
bitset<n> b;       b 有 n 位,每位都 0
bitset<n> b(u);  b 是 unsigned long 型 u 的一个副本
bitset<n> b(s);  b 是 string 对象 s 中含有的位串的副本
bitset<n> b(s, pos, n);  b 是 s 中从位置 pos 开始的&nbps;n 个位的副本。


& :取地址运算符。

* :存储地址。取值运算符。相当于临时中转站。

double x;

x = *pF;       //将pF所指的数据存到变量x

double* pF = &A;   //定义指针pF,同时存入数据A的地址。

指针则可以像迭代器一样用于遍历和检查数组中的元素。

给指针赋值或通过指针进行赋值的区别:

如果对左操作数进行解引用,则修改的是指针所指对象的值;

如果没有使用解引用操作,则修改的是指针本身的值。

例子


函数指针

double Fun(int);   //函数Func( )的原型/* 可以使用下列的语法定义函数指针pF并指向函数Func()*/double (*pF)(int);  //定义函数指针pFpF = Func;             //将函数指针pF指向函数Func( )/*上面两式可以合并成一个具初始化功能的定义语句*/double (*pF) (int) = Func;  //定义函数指针并指向函数Func( )


数组与指针




const 指针

int errNumb = 0;
int *const curErr = &errNumb; // curErr is a constant pointer        "curErr 是指向 int 型对象的const 指针"


定义指向 const 对象的 const 指针:
const double pi = 3.14159;
// pi_ptr is const and points to a const object
const double *const pi_ptr = &pi;

本例中,既不能修改 pi_ptr 所指向对象的值,也不允许修改该指针的指向(即 pi_ptr 中存放的地址值)。 

“pi_ptr 首先是一个 const 指针,指向 double 类型的 const 对象”。


const 成员函数

避免改变该方法数据成员


每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或堆(heap)。

C 语言程序使用一对标准库函数malloc 和 free 在自由存储区中分配存储空间,而 C++ 语言则使用 new 和delete 表达式实现相同的功能。

动态数组的定义

创建数组后,new 将返回指向数组第一个元素的指针。在自由存储区中创建的数组对象是没有名字的,程序员只能通过其地址间接地访问堆中的对象。

int *pia = new int[10];   //此 new 表达式分配了一个含有 10 个 int 型元素的数组,并返回指向该数组第一个元素的指针,此返回值初始化了指针 pia。

二维          (第一维通常称为行(row),第二维则称为列(column))

int ia[3][4] = {              /* 3 elements, each element is an array of size 4 */
        {0, 1, 2, 3} ,         /* initializers for row indexed by 0 */
        {4, 5, 6, 7} ,         /* initializers for row indexed by 1 */
        {8, 9, 10, 11}       /* initializers for row indexed by 2 */
};


vector<int>::iterator iter = ivec.begin();
// prints 10 9 8 ... 1
while (iter != ivec.end())
cout << *iter++ << endl; // iterator postfix increment
自增操作的优先级高于解引用操作,*iter++ 等效于*(iter++)。
子表达式 iter++ 使 iter 加 1,然后返回 iter 原值的副本作为该表达式的结果。因此,解引用操作 * 的操作数是 iter 未加 1 前的副本。

cout << *iter++ << endl;
 等效于:
cout << *iter << endl;
++iter;


在delete 之后,重设指针的值

删除指针后,该指针变成悬垂指针。悬垂指针指向曾经存放对象的内存,但该对象已经不再存在了。悬垂指针往往导致程序错误,而且很难检测出来。

一旦删除了指针所指向的对象,立即将指针置为 0,这样就非常清楚地表明指针不再指向任何对象。


显式转换

显式转换也称为强制类型转换(cast),包括以下列名字命名的强制类型转换操
作符:static_cast、dynamic_cast、const_cast 和 reinterpret_cast。

cast-name<type>(expression);

其中 cast-name 为 static_cast、dynamic_cast、const_cast 和
reinterpret_cast 之一,type 为转换的目标类型,而 expression 则是被强制
转换的值。


string::size_type 类型

任何存储 string 的 size 操作结果的变量必须为 string::size_type 类型。特别重要的是,还要把 size 的返回值赋给一个 int 变量。


是非 const 引用形参(第 2.5 节)只能与完全同类型的非 const 对
象关联。


数组形参的定义

将使用数组语法定义的形参看作指向数组元素类型的指针

// three equivalent definitions of printValues
void printValues(int*) { /* ... */ }
void printValues(int[]) { /* ... */ }
void printValues(int[10]) { /* ... */ }
形参类型都是 int*。

递归
直接或间接调用自己的函数称为递归函数.


const 成员函数的引入

跟在 Sales_item 成员函数声明的形参表后面的 const 所起的作用了:const 改变了隐含的 this 形参的类型。


复制实参的局限性
复制实参并不是在所有的情况下都适合,不适宜复制实参的情况包括:
1. 当需要在函数中修改实参的值时。
2. 当需要以大型对象作为实参传递时。对实际的应用而言,复制对象所付出
的时间和存储空间代价往往过在。
3. 当没有办法实现对象的复制时。

const 形参
1. 如果函数具有普通的非 const 引用形参,则显然不能通过 const 对象进行调用。
2.在调用函数时,如果该函数使用非引用的非 const 形参,则既可给该函数
传递 const 实参也可传递非 const 的实参。
因为初始化复制了初始化式的值,所以可用 const 对象初始化非 const 对象。

当需要在函数中修改实参的值时,需要将形参定义为引用类型。
eg:void swap(int &v1, int &v2){}

传递指向指针的引用

通过引用传递数组
数组形参可声明为数组的引用。如果形参是数组的引用,编译器不会将数组实参转化为指针,而是传递数组的引用本身。在这种情况下,数组大小成为形参和实参类型的一部分。

函数不能仅仅基于不同的返回类型而实现重载。


重载和const 形参

如果形参是普通的引用,则不能将 const 对象传递给这个形参。如果传递了const 对象,则只有带 const 引用形参的版本才是该调用的可行函数。

如果传递的是非 const 对象,非 const 对象既可用于初始化 const 引用,也可用于初始化非 const 引用。但是,将 const 引用初始化为非 const 对象,需通过转换来实现,而非 const 形参的初始化则是精确匹配。


将关键字 const 加在形参表之后,就可以将成员函数声明为常量:double avg_price() const;

const 成员不能改变其所操作的对象的数据成员。


可变数据成员(mutable data member)

永远都不能为 const,甚至当它是const 对象的成员时也如此。

将数据成员声明为可变的,必须将关键字 mutable 放在成员声明之前。


构造函数

构造函数是特殊的成员函数,只要创建类类型的新对象,都要执行构造函数。
构造函数的工作是保证每个对象的数据成员具有合适的初始值。

必须对任何 const 或引用类型成员以及没有默认构造函数的类类型的任何成员使用初始化式。


复制构造函数

Sales_item::Sales_item(const Sales_item rhs);    //error
错,因为每当以传值方式传递参数时,会导致调用复制构造函数:会导致复制构造函数的无穷递归调用。

所以,复制构造函数的形参必须是一个引用,即以传址方式传递参数。


操作符重载

● 调用类中的操作符重载函数的方法:
比如在类hyong中重定义的+操作符 hyong operator +(hyong m){},有类hyong的对象m和n则调用操作符重载函数的方法有m+n和m.operator +(n),前一条语句会自动转换为后面这条语句,且m+n的表达式中最左边的对象是调用操作符重载函数的对象,而最右边的那个将被作为参数传送。

● 调用友元或独立的操作符重载函数的方法:

比如有友元或独立操作符重载函数hyong operator +(hyong a,hyong b){}则当出现m+n时会转换成语句operator +(m,n)表达式的第一个对象传给第一个参数,第二个对象传给第二个参数。


重载操作符与内置操作符的不同之处在于:重载操作符必须具有至少一个类类型或枚举类型的操作数;

重载操作符不保证操作符的求职顺序。

  • eg:       两个语句都将 item2 的值加至 item1

             item1 += item2; // expression based "call"

             item1.operator+=(item2); // equivalent call to member operatorfunction

选择成员或非成员实现

IO 操作符通常对非公用数据成员进行读写,因此,类通常将 IO操作符设为友元。

• 赋值(=)、下标([])、调用(())和成员访问箭头(->)等操作符必须定义为成员。

• 复合赋值操作符(+=)通常应定义为类的成员是不一定的。

• 改变对象状态或与给定类型紧密联系的其他一些操作符,如自增、自减和解引用,通常就定义为类成员。

• 对称的操作符,如算术操作符、相等操作符、关系操作符和位操作符(IO 操作符( <<  >> )),最好定义为普通非成员函数。


protected成员,可以被类成员、友元、派生类(非友元)访问。


句柄类

如果虚函数的基类实例返回类类型的引用或指针,则该虚函数的派生类实例可以返回基类实例返回的类型的派生类(或者是类类型的指针或引用)。


DLL

DLL定义和调用的一般概念:
 (1)DLL中需以某种特定的方式声明导出函数(或变量、类);
 (2)应用工程需以某种特定的方式调用DLL的导出函数(或变量、类)。

DLL的调用方式

动态调用方式的特点是完全由编程者用 API 函数加载和卸载 DLL,程序员可以决定 DLL 文件何时加载或不加载,显式链接在运行时决定加载哪个 DLL文件。

静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时DLL 的卸载。
当调用某DLL的应用程序结束时,若系统中还有其它程序使用该DLL,则Windows对DLL的应用记录减1,直到所有使用该DLL的程序都结束时才释放它。

程序员在建立一个DLL文件时,连接器会自动为其生成一个对应的.lib文件,该文件包含了DLL 导出函数的符号名及序号(并不含有实际的代码)。在应用程序里,.lib文件将作为DLL的替代文件参与编译。

当程序员通过静态链接方式编译生成应用程序时,应用程序中调用的与.lib文件中导出符号相匹配的函数符号将进入到生成的EXE 文件中,.lib文件中所包含的与之对应的DLL文件的文件名也被编译器存储在EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息发现并加载DLL,然后通过符号名实现对DLL 函数的动态链接。这样,EXE将能直接通过函数名调用DLL的输出函数,就象调用程序内部的其他函数一样。

__stdcall约定
  如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而__cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。



========================================================================================================
IO 对象不可复制或赋值
由于流对象不能复制,因此不能存储在 vector(或其他)容器中(即不存在存储流对象的 vector 或其他容器)。


// iter is the iterator type defined by list<string>
list<string>::iterator iter;      将 iter 声明为 iterator 类型,而 iterator 是存放 string 类型元素的 list 类的成员。

原创粉丝点击