今天写代码的时候,发现我写的对象数组,只声明,而没有初始化,所以记录一下这个问题:null reference。
Animals [] an=new Animals[5];//这只是个对象类型数组的声明用的时候
需要
for(int i=0;i<5;i++)an[i]=new Animals();
进行初始化!
所以报null reference的错误,去看下容器是否初始化,也适用于list类型。
今天写代码的时候,发现我写的对象数组,只声明,而没有初始化,所以记录一下这个问题:null reference。
Animals [] an=new Animals[5];//这只是个对象类型数组的声明用的时候需要
for(int i=0;i<5;i++)an[i]=new Animals();进行初始化!
所以报null reference的错误,去看下容器是否初始化,也适用于list类型。
转载于:https://www.cnblogs.com/H-BolinBlog/p/5558109.html
有没有办法初始化一个形状的numpy数组并添加?我将用列表示例解释我需要的。如果我想创建一个循环中生成的对象列表,我可以做:
a = []
for i in range(5):
a.append(i)
我想做一个类似于numpy数组。我知道vstack,连接等。但是,似乎这些需要两个numpy数组作为输入。我需要的是:
big_array # Initially empty. This is where I don't know what to specify
for i in range(5):
array i of shape = (2,4) created.
add to big_array
big_array应该有一个形状(10,4)。如何做到这一点?
编辑:
我想补充以下澄清。我知道,我可以定义big_array = numpy.zeros((10,4)),然后填满它。但是,这需要提前指定big_array的大小。我知道在这种情况下的大小,但如果我不是?当我们使用.append函数扩展python中的列表时,我们不需要提前知道它的最终大小。我想知道是否存在类似的事情,从较小的数组创建一个更大的数组,从一个空数组开始。
数组的存储与初始化
一维数组的存储
数组元素在内存中顺次存放,它们的地址是连续的。元素间物理地址上的相邻,对应着逻辑次序上的相邻。
例如:
![]()
一维数组的初始化
在定义数组时给出数组元素的初始值。
列出全部元素的初始值
static int a[10]={0,1,2,3,4,5,6,7,8,9};
可以只给一部分元素赋初值
static int a[10]={0,1,2,3,4};
在对全部数组元素赋初值时,可以不指定数组长度
static int a[]={0,1,2,3,4,5,6,7,8,9};
按行存放,如:
float a[3][4];
可以理解为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JqCLiPKA-1594979262981)(C:\Users\qbzxb\AppData\Roaming\Typora\typora-user-images\image-20200715190630718.png)]
其中数组a的存储顺序为:a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23
将所有初值写在一个{}内,按顺序初始化
static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
分行列出二维数组元素的初值
static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
可以只对部分元素初始化
static int a[3][4]={{1},{0,6},{0,0,11}};
列出全部初始值时,第1维下标个数可以省略
static int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12};
// 或者
static int a[][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
注意:
static
数组中的数据默认初始化为0;注意:数组作为形参时弱化成为指针
示例:使用数组名组函数参数
#include <iostream>
using namespace std;
void rowSum(int a[][4],int nRow){
for(int i=0;i < nRow;i++){
for(int j = 1;j < 4;j++)
a[i][0] += a[i][j];
}
}
int main(){
int table[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6}};
// 输出数组元素
for(int i = 0;i < 3;i++){
for(int j = 0;j < 4;j++)
cout << table[i][j] << " ";
cout << endl;
}
rowSum(table,3); // 调用子函数,计算各行和
// 输出计算结果
for(int i = 0; i < 3; i++)
cout << "Sum of row " << i << table[i][0] << endl;
return 0;
}
定义对象数组
类名 数组名[元素个数];
访问对象数组元素——通过下标访问
数组名[下标].成员名
数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象。
通过初始化列表赋值。
Point a[2]={Point(1,2),Point(3,4)};
如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用默认构造函数)。
// 普通类型的 for 循环
int main(){
int array[3] = {1,2,3};
int *p;
for(p = array; p < array + sizeof(array) / sizeof(int); ++p){
*p += 2;
std::cout << *p << std::endl;
}
return 0;
}
// 基于范围的 for 循环
int main(){
int array[3] = {1,2,3};
for(int & e : array){
e += 2;
std::cout<<e<<std::endl;
}
return 0;
}
语法形式
存储类型 数据类型* 指针名=初始地址;
int* pa = &a;
注意事项
语法形式
指针名 = 地址;
注意:“地址”中存放的数据类型与指针类型必须相符
向指针变量赋的值必须是地址常量或变量,不能是普通整数
例外:整数0可以赋给指针,表示空指针(NULL)。
允许定义或声明指向void 类型
的指针。该指针可以被赋予任何类型对象的地址。
void *general;
0
或者NULL
去表达空指针的问题:nullptr
关键字,是表达更准确,类型安全的空指针运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置。
当指针指向连续存储的同类型数据时,指针与整数的加减运算和自增自减算才有意义。
其意义是指针当前指向位置的前方或后方第n个数据的起始位置。
数组名是数组的首地址,并且是常量,既然是常量,就不能改变其指向的地址。
定义与赋值
int a[10],*pa;
pa = a; // 或 pa = &a[0]; &是地址运算符
*pa就是a[0],*(pa+1)就是a[1],... ,*(pa+i)就是a[i].
a[i], *(pa+i), *(a+i), pa[i]都是等效的。
注意:不能写 a++,因为a是数组首地址、是常量。
#include
using namespace std;
int main() {
int line1[] = { 1, 0, 0 }; //矩阵的第一行
int line2[] = { 0, 1, 0 }; //矩阵的第二行
int line3[] = { 0, 0, 1 }; //矩阵的第三行
//定义整型指针数组并初始化
int *pLine[3] = { line1, line2, line3 };
cout << "Matrix test:" << endl;
//输出矩阵
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
cout << pLine[i][j] << " ";
cout << endl;
}
return 0;
}
输出结果为:
Matrix test:
1,0,0
0,1,0
0,0,1
指针数组与二维数组对比
int array2[3][3] ={ { 1,0,0 }, { 0,1,0 }, { 0,0,1 } };
可以看出,指针数组和普通数组相比,指针数组可以构建在相对灵活的内存空间中,不同指针所指向的数组间不必连续。而普通数组的所有元素则是存放在一个连续的内存空间中。
思考,高维数组本质上可以看作是广义的一维数组。在这个广义的一维数组中,每一个元素可以被看作是一个一维及以上的数组。
为什么需要用指针作参数?
指针函数的定义形式:
存储类型 数据类型 *函数名()
{ //函数体语句
}
注意:
正确的例子1:
#include
using namespace std;
int main(){
int array[10]; //主函数中定义的数组
int* search(int* a, int num);
for(int i=0; i<10; i++)
cin>>array[i];
int* zeroptr= search(array, 10); //将主函数中数组的首地址传给子函数
return 0;
}
int* search(int* a, int num){ //指针a指向主函数中定义的数组
for(int i=0; i<num; i++)
if(a[i]==0)
return &a[i]; //返回的地址指向的元素是在主函数中定义的
}//函数运行结束时,a[i]的地址仍有
正确的例子2:
#includeusing namespace std;
int main(){
int* newintvar();
int* intptr= newintvar();
*intptr=5; //访问的是合法有效的地址
delete intptr; //如果忘记在这里释放,会造成内存泄漏
return 0;
}
int* newintvar (){
int* p=new int();
return p; //返回的地址指向的是动态分配的空间
}//函数运行结束时,p中的地址仍有效
定义形式:
存储类型 数据类型 (*函数指针名)();
所以定义函数指针时,不仅要声明函数指针名,还要声明函数的返回类型和函数的参数列表
含义:函数指针指向的是程序代码存储区
通过函数指针调用的函数
例如将函数的指针作为参数传递给一个函数,使得在处理相似事件的时候,可以灵活的使用不同方法。
调用者不关心谁是被调用者
对象指针定义形式:类名 *对象指针名;
// 举例
Point a(5,10);
Piont *ptr;
ptr=&a;
通过指针访问对象成员:对象指针名->成员名
ptr->getx() 相当于 (*ptr).getx(); // 这两种方式都可以访问到对象成员
示例:使用指针来访问Point类的成员
#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; //定义对象指针,用a的地址初始化
cout << p1->getX() << endl; //*******用指针访问对象成员
cout << a.getX() << endl; //**********用对象名访问对象成员
return 0;
}
例如:Point类的getX函数中的语句:
return x;
相当于:
return this->x;
曾经出现过的错误例子:
class Fred; //前向引用声明
class Barney {
Fred x; //错误:类Fred的声明尚不完善
};
class Fred {
Barney y;
};
正确的程序:
正确的程序
class Fred; //前向引用声明
class Barney {
Fred *x; // 指针本身的大小是确定的,因此这里的声明没有问题
};
class Fred {
Barney y;
};
要定义一个引用变量p,使之引用类MyClass的一个对象,正确的定义语句是( )。
正确答案D | |
---|---|
A | MyClass p=MyClass; |
B | MyClass p=new MyClass; |
C | MyClass& p=new MyClass; |
D | MyClass a,&p=a; |
【解析】
A项错误,不能直接使用类给对象赋值;B项错误,p是类的对象,不能用对象的指针给其赋值;C项错误,p是引用变量,不能使用对象的指针进行赋值;D项正确,定义了MyClass类的一个对象a和引用变量p,并且声明p初始化为对a的引用。
动态申请内存操作符 new
new 类型名T(初始化参数列表)
释放内存操作符delete
delete 指针p
示例:动态创建对象
#include <iostream>
using namespace std;
class Point {
public:
Point() : x(0), y(0) {
cout<<"Default Constructor called."<<endl;
}
Point(int x, int y) : x(x), y(y) {
cout<< "Constructor called."<<endl;
}
~Point() { cout<<"Destructor called."<<endl; }
int getX() const { return x; }
int getY() const { return y; }
void move(int newX, int newY) {
x = newX;
y = newY;
}
private:
int x, y;
};
int main() {
cout << "Step one: " << endl;
Point *ptr1 = new Point; //调用默认构造函数
delete ptr1; //删除对象,自动调用析构函数
cout << "Step two: " << endl;
ptr1 = new Point(1,2);
delete ptr1;
return 0;
}
运行结果:
Step One:
Default Constructor called.
Destructor called.
Step Two:
Constructor called.
Destructor called.
new 类型名T [ 数组长度 ]
delete[] 数组名p
示例:动态创建对象数组
#include<iostream>
using namespace std;
class Point { //类的声明和上一示例相同,略 };
int main() {
Point *ptr = new Point[2]; //创建对象数组
ptr[0].move(5, 10); //通过指针访问数组元素的成员
ptr[1].move(15, 20); //通过指针访问数组元素的成员
cout << "Deleting..." << endl;
delete[] ptr; //删除整个对象数组
return 0;
}
运行结果:
Default Constructor called.
Default Constructor called.
Deleting...
Destructor called.
Destructor called.
预警,本节理解起来有点抽象,但务必理解
创建格式:new 类型名T[第1维长度][第2维长度]…;
如果内存申请成功,new
运算返回一个指向新分配内存首地址的指针(这个指针指向第一个广义元素的地址)。
char (*fp)[3]; // 这个语句等价于:char ch[3]; char *fp = ch;
fp = new char[2][3];
理解:任何高维数组(假设数组标记为(n,m,j,…)),本质上都可以看作是广义的一维数组,也就是任何一个(n,m,j,…)数组本质上就是由n个(m,j,…)子数组组成。
示例:动态创建多位数组
#include <iostream>
using namespace std;
int main() {
// 请掌握这种动态创建数组的方式,(*p)[][]表示创建了变量指针指向的多维数组,这个指针变量 p 指向new出来的第一个广义元素[9][8]
// 因为new返回的就是广义一维数组的第一个广义元素的地址,且new返回的地址必须和等号左边接受地址的指针的类型匹配,否则编译器会报错
int (*cp)[9][8] = new int[7][9][8];
for (int i = 0; i < 7; i++)
for (int j = 0; j < 9; j++)
for (int k = 0; k < 8; k++)
*(*(*(cp + i) + j) + k) =(i * 100 + j * 10 + k);
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 9; j++) {
for (int k = 0; k < 8; k++)
cout << cp[i][j][k] << " ";
cout << endl;
}
cout << endl;
}
delete[] cp;
return 0;
}
再看一个举例:
int a[2][3]={1,2,3,4,5,6};
int (*fp)[3]; // 整体上看是定义了一个含有三个元素的一维数组。再分析,fp是一个指针,指向这个一维数组。
fp = a;
// 从广义一维数组的角度讲,*fp、*a、fp[0]、a[0]四个操作都抵达了广义一维数组的第一个广义元素(在这里就是一维数组):{1,2,3}
// 也就是说此时的操作层面已降低至一维数组{1,2,3}的内部,但此时指向的依然是地址,也可以理解为返回的依然是地址。
// *(*fp+i)、*(*a+i)、fp[0][i]、a[0][i]就是在{1,2,3}层面上偏移i元素宽度的字节,此时返回的就是具体的数值。
int a[2][3]={1,2,3,4,5,6};
int (*fp)[3];
fp = a;
cout << "fp[0]\t" << fp[0] << endl; // a代表指向数组第一个元素的指针
cout << "*fp\t" << *fp << endl;
cout << "**a\t" << **a << endl; //
cout << "a+1:\t" << a+1 << endl;
cout << "&a+1:\t" <<&a+1 << endl;
cout << "**(a+1)\t" << **(a+1) << endl;
cout << "*(&a+1)\t" << *(&a+1) << endl;
cout << "**(*(&a+1)-1)\t" << **(*(&a+1)-1) << endl;
cout << "==========================\n";
cout << "fp\t" << fp << endl;
cout << "*fp\t" << *fp << endl;
cout << "**fp\t" << **fp << endl;
cout << "*(*fp+1)\t" << *(*fp+1) << endl;
cout << "**(fp+1)\t"<< **(fp+1) << endl;
cout << "=================" << endl;
cout << "sizeof(a)\t" << sizeof(a) << endl;
cout << "sizeof(fp)\t" << sizeof(fp) << endl;
cout << "sizeof(*a)\t" << sizeof(*a) << endl;
cout << "sizeof(*fp)\t" << sizeof(*fp) << endl;
cout << "sizeof(**a)\t" << sizeof(**a) << endl;
cout << "sizeof(**fp)\t" << sizeof(**fp) << endl;
// 结果:
fp[0] 0x63fe00
*fp 0x63fe00
**a 1
a+1: 0x63fe0c
&a+1: 0x63fe18
**(a+1) 4
*(&a+1) 0x63fe18
**(*(&a+1)-1) 4
==========================
fp 0x63fe00
*fp 0x63fe00
**fp 1
*(*fp+1) 2
**(fp+1) 4
=================
sizeof(a) 24
sizeof(fp) 8
sizeof(*a) 12
sizeof(*fp) 12
sizeof(**a) 4
sizeof(**fp) 4
a[i] = *(a+i)
,a本质上就是指针,只不过是一个常量指针(数组名都是常量指针)a
的意义就是存放着指向整个二维数组的首地址,请注意,是指向二维数组的首地址,并没有说是指向了二维数组的第一个元素的地址,尽管二维数组的首地址和该二维数组的第一个元素的地址的字面值一样,但是含义和性质不在一个层面。
*a
不会直接取到第一个元素1
这个值fp
的意义就是存放着指向了二维数组的第一个含有三个元素的一维数组的首地址,请注意,是指向一维数组的首地址,并没有说是指向了一维数组的第一个元素的地址,尽管该一维数组的首地址和该一维数组的第一个元素的地址的字面值一样,但是含义和性质不同。
*fp
不会直接取到第一个元素1
这个值优势:
示例:动态数组类
#include <iostream>
#include <cassert>
using namespace std;
class Point { //类的声明之前相同 … };
class ArrayOfPoints { //动态数组类
public:
ArrayOfPoints(int size) : size(size) { // 构造函数,构造是创建动态数组
points = new Point[size];
}
~ArrayOfPoints() { // 退出前调用析构函数删除动态分配的数组对象
cout << "Deleting..." << endl;
delete[] points;
}
Point& element(int index) { // 获得指定元素,并检查下表是否越界
assert(index >= 0 && index < size);
return points[index];
}
private:
Point *points; //指向动态数组首地址
int size; //数组大小
};
int main() {
int count;
cout << "Please enter the count of points: ";
cin >> count;
ArrayOfPoints points(count); //创建数组对象
points.element(0).move(5, 0); //访问数组元素的成员
points.element(1).move(15, 20); //访问数组元素的成员
return 0;
}
运行结果:
Please enter the number of points:2
Default Constructor called.
Default Constructor called.
Deleting...
Destructor called.
Destructor called.
为什么element函数返回对象的引用?
返回“引用”可以用来操作封装数组对象内部的数组元素。如果返回“值”则只是返回了一个“副本”,通过“副本”是无法操作原来数组中的元素的。
为什么需要vector?
vector对象的定义
vector<元素类型> 数组对象名(数组长度);
vector<int> arr(5)
// 建立大小为5的int数组
vector对象的使用
vector对象名 [ 下标表达式 ]
数组对象名.size()
vector应用举例:
#include <iostream>
#include <vector>
using namespace std;
//计算数组arr中元素的平均值
double average(const vector<double> &arr){
double sum = 0;
for (unsigned i = 0; i<arr.size(); i++)
sum += arr[i];
return sum / arr.size();
}
int main() {
unsigned n;
cout << "n = ";
cin >> n;
vector<double> arr(n); //创建数组对象
cout << "Please input " << n << " real numbers:" << endl;
for (unsigned i = 0; i < n; i++)
cin >> arr[i];
cout << "Average = " << average(arr) << endl;
return 0;
}
//基于范围的for循环配合auto举例
#include <vector>
#include <iostream>
int main(){
std::vector<int> v = {1,2,3}; // 为什么可以这样赋初值???
for(auto i = v.begin(); i != v.end(); ++i)
std::cout << *i << std::endl;
for(auto e : v)
std::cout << e << std::endl;
}
string(); //默认构造函数,建立一个长度为0的串
string s1;
string(const char *s);
//用指针s所指向的字符串常量初始化string对象
string s2 = “abc”;
string(const string& rhs);
//复制构造函数
string s3 = s2;
示例1: string类应用举例
#include <string>
#include <iostream>
using namespace std;
//根据value的值输出true或false
//title为提示文字
inline void test(const char *title, bool value)
{
cout << title << " returns "
<< (value ? "true" : "false") << endl;
}
int main() {
string s1 = "DEF";
cout << "s1 is " << s1 << endl;
string s2;
cout << "Please enter s2: ";
cin >> s2;
cout << "length of s2: " << s2.length() << endl;
//比较运算符的测试
test("s1 <= \"ABC\"", s1 <= "ABC");
test("\"DEF\" <= s1", "DEF" <= s1);
//连接运算符的测试
s2 += s1;
cout << "s2 = s2 + s1: " << s2 << endl;
cout << "length of s2: " << s2.length() << endl;
return 0;
}
结果:
s1 is DEF
Please enter s2: DFFA
length of s2: 4
s1 <= "ABC" returns false
"DEF" <= s1 returns true
s2 = s2 + s1: DFFADEF
length of s2: 7
考虑:如何输入整行字符串?
cin
的>>
操作符输入字符串,会以空格作为分隔符,空格后的内容会在下一回输入时被读取输入整行字符串
getline
可以输入整行字符串(要包string头文件),例如:getline(cin, s2);
getline(cin, s2, ',');
例6-24 用getline输入字符串
include <iostream>
#include <string>
using namespace std;
int main() {
for (int i = 0; i < 2; i++){
string city, state;
getline(cin, city, ','); //以 逗号 作为分隔符
getline(cin, state);
cout << "City:" << city << “ State:" << state << endl;
}
return 0;
}
运行结果:
Beijing,China
City: Beijing State: China
San Francisco,the United States
City: San Francisco State: the United States
例:“program”
各字符连续、顺序存放,每个字符占一个字节,以‘\0’结尾,相当于一个隐含创建的字符常量数组
“program”
出现在表达式中,表示这一char数组的首地址
首地址可以赋给char常量指针:
const char *STRING1 = "program";
char str[8] = { 'p', 'r', 'o', 'g', 'r', 'a', 'm', '\0' };
char str[8] = "program";
char str[] = "program";
用字符数组表示字符串的缺点
1.对象数组的使用
对象也可以用数组来存放,必须有两个步骤:(1)声明类类型的数组变量,并用new分配内存空间给数组(2)用new产生新的对象,并分配内存空间给它
例:要创建三个Car类类型的数组元素
Car c[]=new Car[3];//创建Car类类型的数组元素,并 分配内存空间
动态初始化,利用for循环完成对象数组内的初始化操作
例: for(int i=0;i<c.length;i++){
c[i]=new Car();
}
静态初始化
例: Car c[]={new Car(),new Car(),new Car()};
public class Car { String color; String name; Car(String c,String n){ color=c; name=n; } String show(){ return "这是"+color+name; } }public class car1 { public static void main(String[] args) { Car c[]={new Car("红色","奥迪"),new Car("白色","大众"),new Car("黑色","奔驰")}; for(int i=0;i<c.length;i++){ System.out.println(c[i].show()); } } }运行结果:
这是红色奥迪
这是白色大众
这是黑色奔驰2.内部类
内部类的基本格式:
标识符 class 外部类名称{
//外部类的成员
标识符 class 内部类名称{
//内部类的成员
}
}
内部类可以直接调用外部类中的属性,外部类不可以访问内部类声明的属性。内部类用static声明后,内部类不能访问非static定义的外部类属性
public class out1 { int score; void show(){ in1 in=new in1(); in.talk(); } class in1{ void talk(){ System.out.println("他的分数是;"+score); } } }public class out11 { public static void main(String[] args) { out1 out=new out1(); out.score=98; out.show(); } }3.在类外部引用内部类
内部类可以通过创建对象从外部类之外被调用,只要将内部类声明成public。
public class out1 { int score; void show(){ in1 in=new in1(); in.talk(); } public class in1{ void talk(){ System.out.println("他的分数是;"+score); } } }public class out11 { public static void main(String[] args) { out1 out=new out1(); out1.in1 in=out.new in1();//用外部类的对象实例化内部类的一个对象 out.score=98; in.talk(); } }4.在方法中定义内部类
public class out1 { int score; void show() { class in1 { void talk() { System.out.println("他的分数是;" + score); } } in1 in = new in1(); in.talk(); } }public class out11 { public static void main(String[] args) { out1 out=new out1(); out.score=98; out.show(); } }在方法中定义的内部类只能访问方法中的final类型的 局部变量,因为用final定义的局部变量相当于是一个常量,它的生命周期超出方法运行的生命周期
public class out1 { int score; void show(final String s) { final String name="lindy"; class in1 { void talk() { System.out.println(name+s+"的分数是;" + score); } } in1 in = new in1(); in.talk(); } }public class out11 { public static void main(String[] args) { out1 out=new out1(); out.score=98; out.show("数学"); } }
转载于:https://www.cnblogs.com/dongdong-wang/p/6699318.html