【C++复习总结回顾】—— 【五】数组与指针

需要更好的阅读的体验请移步 👉 小牛肉的个人博客 👈



一、数组

1. 数组的存储与初始化

一维数组

二维数组

2. 数组作为函数参数

3. 对象数组


对象数组——线性回归方程实例

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;
};
//直线线性拟合,number为点数
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;
	//输出回归系数a,b
    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 * function(){
}

函数类型的指针

即指向函数的指针

定义:数据类型 (* 函数指针名)(形参表)
int (*func)(int, int)
含义:函数指针指向的是程序代码存储区。


#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. 对象指针

定义形式:类名 *对象指针名

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 动态分配

动态分配一个变量

p = new T; //T代表int,double,float等等
//p类型为T*的指针
//该语句动态分配了一片大小为 sizeof(T) 字节的内存空间,并将起始地址赋给了p
int *pn;
pn = new int;
*pn = 5; //往刚开辟的空间中写入了数据5

动态分配一个数组

p = new T[N];
//动态分配了一片大小为 sizeof(T)*N 字节的内存空间,并将起始地址赋给了p
int *pn;
int i = 5;
pn = new int[i*20]; //100个元素,最大合法下标为99
pn[0] = 20;
pn[100] = 30; // 编译没问题,运行时数组越界

注:new运算符返回值类型都是 T*

delete 释放动态分配的内存

delete总是和new成对出现 :即动态分配的内存一定要释放掉,否则占用的内存就会越来越多。

delete指针
该指针必须指向new出来的空间

int *p = new int;
*p = 5;
delete p;
delete p; //wrong; 同一片空间不能被delete两次

delete [ ]指针

int *p = new int[20];
p[0] = 1;
delete []p; //若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,具有构造函数、析构函数、复制构造函数、并重载运算符 +、=、+=、[ ],尽可能的完善它,使之能满足各种需要。

#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); //a+b a的值并不会被修改,不需要返回引用
        void operator +=(const Mystring& rhs); //a+=b 无返回值
        Mystring& operator =(const Mystring& rhs); //a=b a的值会被修改,需要返回引用
};
//无参构造
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;
}

测试:

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;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞天小牛肉

您的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值