数组
数组的存储
数组元素在内存中是顺序的、连续存储的
数组的初始化
第一维的下标个数可以不用显式说明(因为可以根据后面的维度算出来)
数组也可以被声明常量
const float fa[5]={1.0,2.0,3.0}
表明fa数组中每个元素都被当作常量对待,也就是说它们的值在初始化后皆不可以改变
数组作为函数参数
void a(int a[4]){
cout<<a[0];
}
int main(){
int b[3];
int c[4];
int d[5];
a(b);
a(c);
a(d);
}
上面代码中b,c,d都能正常运行(数组函数作形参,第一维的数可写可不写;其余维的数必须要写)
对象数组
location a[2]={location(1,2),location(3,4)};
注意一下——元素构造函数用的是类名+参数
访问公有成员,一般形式:
数组名[下标表达式].成员名
不指定初始值,就会调用默认构造函数
当各元素对象的初值要求为相同的值时,可以在类中定义含默认值的构造函数
指针
指针类型
1.可以声明指向常量的指针,此时不能通过指针来改变所致对象的值,但指针本身可以改变,可以指向另外的对象
const int*p1=&a;
2.可以声明指针类型的常量,这时指针本身的值不能改变
int *const p2=&a;
3.void类型指针,可以存储任何类型的对象地址,就是说任何类型的指针都可以赋值给void类型的指针变量。经过使用类型显式转换,通过void类型的指针便可以访问任何类型的数据(void指针一般只在指针所指向的数据类型不确定时使用)
指针运算
*(p1+n1)等于p1[n1]
指针数组
int *p[3]由于指针数组的每个元素都是一个指针,必须先赋值后引用,因此,声明数组之后,对指针元素赋初值是必不可少的
int line1[]={1,0,0};
int line2[]={0,1,0};
int line3[]={0,0,1};
int *pline[3]={line1,line2,line3};
pline[i][j]等价于*(pline[i]+j)
pline类似二维数组
用指针作为函数参数
当需要在不同的函数之间传送大量数据时,程序执行时调用函数的开销就会比较大。这时如果需要传递的数据是存放在一个连续的内存区域中,就可以值传递数据的起始地址,而不必传递数据的值,这样就会减少开销,提高效率
指针型函数
使用指针型函数的最主要目的是要在函数结束时把大量的数据从被调函数返回到主调函数中
对象指针
对象指针名->成员名
this指针
this指针隐含于每一个类的非静态成员函数中的特殊指针,它用于指向正在被成员函数操作的对象
指向类的非静态成员的指针
类型说明符 类名::*指针名
类型说明符 (类名::*指针名)(参数表)
对类成员取地址时,也要遵守访问权限的约定,也就是说,在一个类的作用域之外不能够对它的私有成员取地址
指针名=&类名::数据成员名
指针名=&类名::函数成员名
对象名.*类成员指针名
对象指针名->*类成员指针名
(对象名.*类成员指针名)(参数表)
(对象指针名->*类成员指针名)(参数表)
#include<iostream>
using namespace std;
class point
{
public:
point(int x=0,int y=0):x(x),y(y){}
int getx() const {return x;}
int gety() const {return y;}
private:
int x,y;
};
int main()
{
point a(4,5);
point *p1=&a;//定义对象指针并初始化
int (point::*funcptr)() const=&point::getx;//定义成员函数指针并初始化
cout<<(a.*funcptr)()<<endl;//使用成员函数指针和对象名访问成员函数
cout<<(p1->*funcptr)()<<endl;//使用成员函数指针和对象指针访问成员函数
cout<<a.getx()<<endl;//使用对象名访问成员函数
cout<<p1->getx()<<endl;//使用对象指针访问成员函数
return 0;
}
指向类的静态成员的指针
对于类的静态成员的访问是不依赖于对象的,因此可以用普通的指针来指向和访问静态成员
动态内存分配
动态内存分配技术可以保证出现在运行的过程中按照实际需要申请适量的内存,使用结束后可以释放,这种在程序运行过程中申请和释放的存储单元也称为堆对象。申请和释放过程一般称为建立和删除。
new
首先,第一句话,new完记得delete,否则可能会内存泄漏
用一段代码还是更加形象一点
#include <bits/stdc++.h>
using namespace std;
int example1()
{
//可以在new后面直接赋值
int *p = new int(3);
//也可以单独赋值
//*p = 3;
//如果不想使用指针,可以定义一个变量,在new之前用“*”表示new出来的内容
int q = *new int;
q = 1;
cout << q << endl;
return *p;
}
int* example2()
{
//当new一个数组时,同样用一个指针接住数组的首地址
int *q = new int[3];
for(int i=0; i<3; i++)
q[i] = i;
return q;
}
struct student
{
string name;
int score;
};
student* example3()
{
//这里是用一个结构体指针接住结构体数组的首地址
//对于结构体指针,个人认为目前这种赋值方法比较方便
student *stlist = new student[3]{{"abc", 90}, {"bac", 78}, {"ccd", 93}};
return stlist;
}
int main()
{
int e1 = example1();
cout <<"e1: "<< e1 << endl;
int *e2 = example2();
for(int i=0; i<3; i++)
cout << e2[i] << " ";
cout << endl;
student *st1 = example3();
for(int i=0; i<3; i++)
cout << st1[i].name << " " << st1[i].score << endl;
return 0;
}
new创建类对象
#include <iostream>
using namespace std;
class Test {
private:
public:
add()
{
int x,y,sum;
x=5;
y=5;
sum=x+y;
cout<<sum<<endl;
}
};
void main()
{
Test test1; //栈中分配 ,由操作系统进行内存的分配和管理
Test test2 = Test(); //栈中分配 ,由操作系统进行内存的分配和管理
Test *test3=new Test(); //堆中分配 ,由管理者进行内存的分配和管理,用完必须delete(),否则可能造成内存泄漏
test1.add();
test2.add(); //"." 是结构体成员引用
test3->add(); //"->"是指针引用
delete(test3);
system("pause");
}
这一段话,看得云里雾里的,何为用户定义的默认构造函数😵
我个人看法:为了能够理解,忽略掉用户定义,就理解为默认构造函数,那么这段话就没问题(用代码测试过,没毛病)
加上"()" 相当于要进行默认的初始化(包括后面的new数组也可以这么理解)
new 数组类型对象
注意这个删除的语法:
delete[] 指针名
new 多维数组
new 类型名T[数组第1维长度][数组第2维长度]
delete
delete只是释放内存空间,不会删掉指针(p=new int,delete p,在这之后,这个p还是能用的,可以继续p=new int)
assert检查
release模式比debug模式效率、速度要高
vector创建数组对象
vector<元素类型>数组对象名
深复制、浅复制
浅复制——例如,调用默认的复制构造函数时,会实现对应数据项的直接复制。此时,两个对象的成员具有相同值,但是两个对象的指针指向的是同一内存地址(两个对象共用了同一块内存空间)。对其中一个对象进行操作时,会影响另一个对象。
在程序结束之前,两个对象的析构函数会被自动调用,此时,两个对象所指的同一块空间会被两次释放,导致运行错误。
深复制——编写复制构造函数:
上述代码中,为需要的对象开辟了新的内存空间。
string
string大小的比较
常用成员函数功能介绍
getline
cin会将空格作为输入的分隔符
而如果要输入空格的话,就使用getline