一、数组 1. 数组的存储与初始化 一维数组
二维数组
2. 数组作为函数参数
3. 对象数组 对象数组——线性回归方程实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Point { public : Point(float xx = 0 ,float yy = 0 ):x(xx),y(yy){ } float getX () const {return x;} float getY () const {return y;} private : float x,y; }; float lineFit (const Point points[], int number) { float avgx = 0 , avgy = 0 ; float lxx = 0 ,lyy = 0 , lxy = 0 ; for (int i =0 ;i<number;i++){ avgx += points[i].getX()/number; avgy += points[i].getY()/number; } for (int i = 0 ;i<number;i++){ lxy += (points[i].getX() - avgx) * (points[i].getY() - avgy); lyy += (points[i].getY() - avgy) * (points[i].getY() - avgy); lxx += (points[i].getX() - avgx) * (points[i].getX() - avgx); } cout <<"This line can be fitted by y=ax+b." <<endl ; cout <<"a = " <<lxy/lxx<<" " <<"b = " <<avgy-lxy*avgx/lxx<<endl ; return (float )(lxy/sqrt (lxx*lyy)); } int main () { Point points[10 ] = {Point(6 ,10 ),Point(14 ,20 ),Point(26 ,30 ),Point(33 ,40 ), Point(46 ,50 ),Point(54 ,60 ),Point(67 ,70 ),Point(75 ,80 ), Point(84 ,90 ),Point(100 ,100 )}; float r = lineFit(points,10 ); cout <<"Line coefficient r = " <<r<<endl ; }
二、指针 1. 指针运算符 * 和 &
*
称为指针运算符,也称解析;
&
称为取地址运算符;
2. 指针变量的赋值运算
3. 指针的类型 指向常量的指针 const int * p *
在const的右边
指针类型的常量 int * const p *
在const的左边
4. 指针的算法运算、关系运算
5. 用指针访问数组元素
6. 指针数组 Point *p[2]
7. 指针与函数 指针作为函数参数 为什么需要用指针做参数?
需要数据双向传递时(引用也可以达到此效果); 用指针作为函数的参数,可以使被调函数通过形参指针存取主调函数中实参指针指向的数据,实现数据的双向传递
需要传递一组数据,只传首地址运行效率比较高 ; 实参是数组名时,形参可以是指针
引用和指针有何区别?何时只能用指针而不能使用引用
引用是一个别名,不能为空,不能被重新分配 ;指针是一个存放地址的变量。
当需要对变量重新赋以另外的地址或赋值为NULL时只能使用指针
指针类型的函数 若函数的返回值是指针,该函数就是指针类型的函数
函数类型的指针 即指向函数的指针
定义:数据类型 (* 函数指针名)(形参表)
int (*func)(int, int)
含义:函数指针指向的是程序代码存储区。
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <iostream> using namespace std ;int compute (int a, int b, int (*func)(int , int )) { return func(a, b); } int max (int a, int b) {11 return ((a > b) ? a : b); } int min (int a, int b) { return ((a < b) ? a : b); } int sum (int a, int b) { return a + b; } int main () { int a, b, res; cout << "请输入整数a:" ; cin >> a; cout << "请输入整数b:" ; cin >> b; res = compute(a, b, &max ); cout << "Max of " << a << " and " << b << " is " << res << endl ; res = compute(a, b, &min ); cout << "Min of " << a << " and " << b << " is " << res << endl ; res = compute(a, b, &sum); cout << "Sum of " << a << " and " << b << " is " << res << endl ; }
8. 对象指针 定义形式:类名 *对象指针名
1 2 3 Point *ptr; Point a (5 ,10 ) ;ptr = &a;
对象指针通过 ->
访问对象成员
ptr->getx()
相当于 (*ptr).getx();
9. this 指针
10. 动态内存分配
首先我们需要分清几个概念 int *point = new int(5); //用5初始化 int *point = new int(); //用0初始化 int *point = new int; //不分配初值 int *point = new int[5] //表示为该指针分配包含十个元素的地址空间
同一个程序有可能会处理很大的数据,有时处理很小,若总是以最大的空间内存分配,难免会造成浪费。
在C语言中,用 malloc
进行动态内存分配;
在C++中,用 new
语句
new 动态分配 动态分配一个变量
1 2 3 4 5 6 p = new T; int *pn;pn = new int ; *pn = 5 ;
动态分配一个数组
1 2 3 4 5 6 7 p = new T[N]; int *pn;int i = 5 ;pn = new int [i*20 ]; pn[0 ] = 20 ; pn[100 ] = 30 ;
注:new运算符返回值类型都是 T*
delete 释放动态分配的内存 delete总是和new成对出现 :即动态分配的内存一定要释放掉,否则占用的内存就会越来越多。
delete指针
该指针必须指向new出来的空间
1 2 3 4 int *p = new int ;*p = 5 ; delete p;delete p;
delete [ ]指针
1 2 3 int *p = new int [20 ];p[0 ] = 1 ; delete []p;
三、自定义动态数组类 ArrofPoints 将数组的建立和删除过程封装起来,数组下标越界检查
四、Vector类模板(C++标准库中的) 为什么需要vector
封装任何类型的动态数组,自动创建和删除
数组下标越界检查
vector对象的定义 vector<元素类型> 数组对象名(数组长度)
vector<int> arr(5)
建立大小为5的int数组
vector<int> arr(5,2)
大小为5的int类型数组,所有元素用2初始化
vector对象的使用
对数组元素的引用 : 与普通数组具有相同形式: vector对象名 [ 下标表达式 ]
vector数组对象名不表示数组首地址 ,因为数组对象不是数组,而是封装了数组的对象
获得数组长度 :用size函数 数组对象名.size()
vector应用举例:
五、深复制与浅复制
以上面的自定义动态数组类 ArrayOfPoints 为例
浅复制 ArrayOfPoints pointsArray2(pointsArray1); //创建副本
pointsArray1和pointsArray2有相同的值,表面上好像完成了复制,但是指向的是同一个内存空间,并没有形成真正的副本,当程序中改变pointsArray1时也会改变pointsArray2。
而且,在程序结束之前,pointsArray1和pointsArray2的析构函数会自动调用,动态分配的内存空间被释放,由于两个对象给公用了同一块内存空间,因此该空间被释放两次,导致运行错误。
深复制(重新编写复制构造函数) 核心思想:重新new一个存储空间进行值的复制操作
六、字符串 详见第一章 【C++复习总结回顾】—— 【一】基础知识+字符串/string类
七、手撸String类 设计一个字符串类MyString,具有构造函数、析构函数、复制构造函数、并重载运算符 +、=、+=、[ ],尽可能的完善它,使之能满足各种需要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 #include <iostream> #include <cstring> using namespace std ;class Mystring { private : char * mystring; int len; Mystring(int clen){ mystring = new char [clen + 1 ]; for (int i = 0 ; i < clen; i++) mystring[i] = '\0' ; len = clen; } public : Mystring(); Mystring(const char * const cmystring); Mystring(const Mystring& rhs); ~Mystring(); int getLen () const { return this ->len; } const char *GetMyString () const { return mystring; } char & operator [](int offset); char operator [](int offset) const ; Mystring operator +(const Mystring& rhs); void operator +=(const Mystring& rhs); Mystring& operator =(const Mystring& rhs); }; Mystring::Mystring(){ mystring = new char [1 ]; mystring[0 ] = '\0' ; len = 0 ; } Mystring::Mystring(const char * const cmystring){ len = strlen (cmystring); mystring = new char [len+1 ]; for (int i = 0 ; i<len; i++) mystring[i] = cmystring[i]; mystring[len] = '\0' ; } Mystring::Mystring(const Mystring& rhs){ len = rhs.getLen(); mystring = new char [len+1 ]; for (int i = 0 ; i<len;i++) mystring[i] = rhs[i]; mystring[len] = '\0' ; } Mystring::~Mystring(){ delete [] mystring; len = 0 ; } char & Mystring::operator [](int offset){ if (offset>len) return mystring[len-1 ]; else return mystring[offset]; } char Mystring::operator [](int offset) const { if (offset > len) return mystring[len - 1 ]; else return mystring[offset]; } Mystring Mystring :: operator +(const Mystring& rhs){ int totallen = len + rhs.getLen(); Mystring str (totallen) ; int i = 0 ; for (;i<len;i++) str[i] = mystring[i]; for (int j = 0 ;j<rhs.getLen();i++,j++) str[i] = rhs[j]; str[totallen] = '\0' ; return str; } void Mystring::operator +=(const Mystring& rhs){ int totallen = len + rhs.getLen(); Mystring str (totallen) ; int i = 0 ; for (;i<len;i++) str[i] = mystring[i]; for (int j = 0 ; j < rhs.getLen(); i++, j++) str[i] = rhs[j]; str[totallen] = '\0' ; *this = str; } Mystring &Mystring ::operator =(const Mystring &rhs) { delete [] mystring; len = rhs.getLen(); mystring = new char [len + 1 ]; for (int i = 0 ; i < len; i++) mystring[i] = rhs[i]; mystring[len] = '\0' ; return *this ; }
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int main () { Mystring s1 ("initial test" ) ; cout << "S1:\t" << s1.GetMyString() << endl ; char *temp = "Hello World" ; s1 = temp; cout << "Now,S1 is replaced by Hello World" << endl ; cout << "S1:\t" << s1.GetMyString() << endl ; char s2[20 ]; strcpy (s2, "; I like you!" ); cout << "S2:\t" << s2 << endl ; s1 += s2; cout << "S1+S2:\t" << s1.GetMyString() << endl ; cout << "S1[4]:\t" << s1[4 ] << endl ; s1[4 ] = 'x' ; cout <<"now s1[4] is repalced by x" <<endl ; cout << "S1:\t" << s1.GetMyString() << endl ; cout << "S1[999]:\t" << s1[999 ] << endl ; Mystring s3 (" Another myString" ) ; cout << "S3:\t" << s3.GetMyString() << endl ; Mystring s4; s4 = s1 + s3; cout << "S1+S3:\t" << s4.GetMyString() << endl ; return 0 ; }