C++基础知识点(上)

静态成员(static)

  • 静态数据成员的生命周期是整个程序。

  • 类的静态数据成员具有静态生存周期,为该类所有对象共享,必须在类外定义和初始化。
    在这里插入图片描述

  • 静态函数成员
    静态函数成员用来处理静态数据,静态数据不属于任意一个对象,但是可以使用任意对象通过调用成员函数来访问静态数据,存在一个问题就是:如果在没有定义对象的前提下,想访问静态数据,则没有对象依托来调用数据访问函数。所以静态函数成员与普通函数成员不同,一般不是用来处理对象数据的,而专门处理属于整个类的静态数据。

  • 静态函数成员可以通过 类名+作用域分辨符或者对象调用访问静态数据。

  • 静态成员函数不能直接访问对象的变量,要作为参数传入。
    在这里插入图片描述)

const

  • 常成员函数可以被非常对象调用。
  • 常数据成员必须被初始化,并且不能被更新,通过构造函数成员初始化列表进行初始化。

多文件结构和预编译命令

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述)
在这里插入图片描述)
在这里插入图片描述

  • 静态变量和静态函数即使使用extern关键字也只能在定义的文件中使用。

0722 第六章学习

指针相关

  • 整数0是一个例外,可以把它赋值给指针,表示空指针。C语言使用null,C++11中使用nullptr更安全准确的表示空指针。
  • 不能将非静态变量地址赋值给静态指针
  • 区分指向常量的指针【指针指向的对象不能变】和指针类型的常量【指针本身不能变】(211219增)

函数指针:

函数指针即指向函数的指针,定义形式为:
存储类型 数据类型 (*函数指针名)()
【数据类型即指针指向函数返回值的类型】
eg:典型应用,函数回调,将函数指针作为一个函数的参数,通过传入不同功能的函数来处理多种相似的事件。

#include"stdafx.h"
#include<iostream>
using namespace std;
int multifun(int a, int b, int( *funp)(int pa,int pb))
{
	return(funp(a,b));
}
int max(int ma, int mb)
{
	return((ma>mb) ? ma : mb);
}
int sum(int sa, int sb)
{
	return(sa + sb);
}
int main()
{
	int n1, n2;
	int res1, res2;
	cout << "please input n1" << endl;
	cin >> n1;
	cout << "please input n2" << endl;
	cin >> n2;
	res1 = multifun(n1, n2, &max);
	res2 = multifun(n1, n2, &sum);
		cout << "res1=" << res1 << endl;
	cout << "res2=" << res2 << endl;
	return 0;
}

TIPS: funp是一个指针,它指向一个需要两个参数的函数,返回值类型实际是指向函数的返回值类型,如果指针指向函数fun5那么funp(a,b)语句等同于fun5(a,b), int *funp(int a ,int b);表示返回值为指针的函数,int(*p)(int a ,int b);表示指向函数的指针。

对象指针

定义:指向对象的指针。 语法: 类名 *对象指针名
对象成员访问: ptr->getx() 等同于(*ptr).getx();

指针函数

定义:如果函数的返回值是指针,则称该函数为指针类型的函数。
int *fun(){} 【int 代表返回指针所指向地址空间中的数据类型】

指针数组

定义:数组中的元素为指针类型,即指针数组。
3-1,典型应用:二维数组,指针即地址,数组名即地址,数组里面放地址等同于数组里面放数组,即二维数组。如下:

int main()
{
int l1[]={1,0,0};
int l2[]={0,1,0};
int l3[]={0,0,1};
int *ptr[]={l1,l2,l3};
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
cout<<ptr[i][j]<<" ";
cout<<endl;
}
return 0;
}

###指针与数组
数组名代表数组首个元素的地址,是一个地址常量,指针是地址变量,指针的加加操作每次跳跃的内存单元与存储的数据类型有关。如果定义二维数组 int array2[3][3],则双层循环输出可以使用语句:cout<<((array2+i)+j)<<""<<endl;
其中a+i代表到第i行。
在这里插入图片描述

智能指针

void 类型的指针

变量不能定义为void类型的,而指针可以,指针变量的类型是unsigned long int 型的,它存放数组或者其他各种类型数据的首地址,在不需要取出数据时,不需要知道该地址中存放的是什么类型的数据,如果要取内存中的数据,必须知道数据类型,因为要根据类型确定取几个字节的数据。

#include<iostream>
using namespace std;
int main()
{
    // void voidtype  --Not allowed
    int i=10;
    void *pr;
    pr=&i;
    int *pr1;
    pr1=static_cast<int*>(pr);
    cout<<"pr1="<<*pr1<<endl;
    return 0;
}

指向常量的指针

指针本身可以被重新分配地址,但是不能通过该指针改变指向内存中的对象。

#include<iostream>
using namespace std;
int main()
{
    const int *pr1;
    int *pr2;
    int i=10;
    pr1=&i;
    cout<<*pr1<<endl;
    int a=20;
    pr1=&a;
    cout<<*pr1<<endl;
    pr2=&i;
    *pr2=40;
    cout<<*pr2<<"  "<<i<<endl;
    *pr1=30;// eror:assignment of read-only location *pr1
    cout<<*pr1<<endl;
    return 0;
}

指针类型的常量

即:指针常量,如果声明指针常量,那么指针本身的值将不能被改变。

int a;
int *const p=&a;
int b;
p=&b;//错误

指针作为函数的参数

c语言中传大的结构体或者需要返回多个数据时,一般是传它的指针,而C++中有了引用,所以传递大的对象或者需要return一个大的对象、多个数据的时候会使用引用(提前在主函数中准备好多个变量或对象,然后将其地址或引用传给函数在函数中操作)。但是在需要传递一个数组数据的时候,C++中还是使用指针,形参定义一个指针,实参传递一个数组名字。

内存动态分配相关

分配内存
动态内存申请操作符(运算符): new
句法:new 类型明 (参数表)
示例:

#include"Point.h"//假定已经定义Point类
int main()
{
Point *ptr1=new Point;
delete ptr1;  //释放的是指针内存空间,指针本身还存在,后续可以继续使用
ptr1=new Point(1,2);
delete ptr1;
return 0;
}

分配和释放动态数组

句法:new 类型名 [第1维长度][第2维长度]…;delete [] 数组名;
tip:数组长度可以是任意表达式,在运行时计算。

int main() {
	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;

疑问

疑问1 :关于数组指针:

#include"stdafx.h"
#include<iostream>
using namespace std;
int main()
{
	int (*ptrt)[3];
	int a[2][3] = { { 1, 2, 3 }, { 5, 6, 7 } };
	ptrt=a;
	cout << "out put of ptrt is:" << endl;
	for (int i = 0; i < 3; i++){
		for (int j = 0; j < 3; j++)
			cout << ptrt[i][j];
		cout << endl;
	}
	return 0;
}

上面代码:“(*ptrt)[3]” 改为“(*ptrt)[2]” 和 “ptrt[3]”就报错;按我的理解指针数组存储二维数组的行,所以两行因该没错啊,报错为 无法从int[2][3]转化为int※[3]“和无法从int[2][3]转化为int(※)[2]”。
三维数组示例也是的,输出为7个9行8列的矩阵,按理说是7
9个行的地址呀,但是语法要求是多维数组,定义去掉第一维的数组指针。

疑问1理解:多为数组的指针定义时必须加括号,因为多维数组的指针指向的是每一行的首元素地址,指针加1就跳过列数指向下一行,因此指针后[ ]内的数值代表的是每一行共有几列元素,作为一个参数在指针加减操作时知道跳多远。

动态数组类

每次动态数组的建立需要使用运算符new和delete[];且容易遗漏delete,目前认为动态数组类是对类特性及优势的运用:即将具有特性意义的数据和方法封装称模块形成用户自己的数据类型方便运用,就像点、线/圆形等。

  • 思路: 在类的数据成员中定义整型数组长度数据和存放动态分配地址的指针数据;使用构造函数初始化长度数据并分配动态内存空间。

码例(点动态点类数组):

#include"stdafx.h"
#include<cassert>
#include "Point.h"
#include<iostream>
using namespace std;
class Arrayofpoint
{
private:
	int size;
	Point *points;
public:
	Arrayofpoint(){};
	Arrayofpoint(int s) :size(s)
	{
		points = new Point[size];
	}
	~Arrayofpoint()
	{
		delete[] points;
	}
	Point &viewelement(int index)
	{
		assert(index >= 0 && index < size);
			return points[index];
	}
};
int main()
{
	Arrayofpoint p(2);
	p.viewelement(0).move(2, 1);
	p.viewelement(1).move(3, 4);
	cout<<"The x of p[0] is: "<<p.viewelement(0).getx();
	cout << endl;
	cout << "The y of p[1] is: " << p.viewelement(1).gety();
	return 0;
}

结果:
在这里插入图片描述
说明:为何使用&viewelement()函数 ?,答:数组指针points是类的私有数据成员,类外访问时只能通过共有成员函数实现。又由于需要对动态开辟的内存进行处理,故需要传址或者引用不能传值。

动态分配二维数组地址的实现

码例:

#include"stdafx.h"
#include<iostream>
using namespace std;
int main()
{
	int **a;
	a = new int*[2];
	for (int i = 0; i < 2; i++)
		a[i] = new int[3];         //error: *a[i]= new int[3];
    cout << "please input element of a[2][3]:" << endl;
	for (int i = 0; i < 2; i++)
	for (int j = 0; j < 3; j++)
		cin >> a[i][j];
	cout << "The array of a[2][3] is :" << endl;
	for (int i=0; i < 2; i++){
	for (int j = 0; j < 3; j++)
		cout << a[i][j];
	cout << endl;
}
	return 0;
}

说明:二维数组即元素为行数组的列数组。

Vector

vector 是c++标准模板库(STL)中的一个类模板。它对应于数组。
由于点的动态内存数组类只能定义存储点的数组,如果想存储int或者其他类型数据就需要再定义一个类,比较麻烦,因此有了模板类,而vertor就是类似于动态数组的模板类。
语法:vector<元素类型>对象名**(数组长度)**

  • vector 数组对象名不表示数组首地址
  • 除了分配存储空间,STL容器还提供了一些方法,其中size()用于返回容器中元素数目。
    码例:
#include"stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
double average(const vector<int> &ar)
{   double sum = 0;
	for (int i = 0; i < ar.size(); i++)
		sum += ar[i];
	return sum / ar.size();}
int main()
{
	int n;
cout << "please input the length of array a:" << endl;
	cin >> n;
	vector<int>a(n);
	for (int i = 0; i < n; i++)
		cin >> a[i];
	cout << "average of n number is :" << average(a);
	return 0;
}

说明:码例中为何可以使用“cin>>a[i]”直接访问类私有数据成员,而不是动态点类那样使用viewelement。实际上VERTOR中对下标运算符[]已经进行了重载。

基于范围的for循环

码例1:

#include"stdafx.h"
#include<iostream>
using namespace std;
int main()
{
	int array[3] = { 1, 2, 3 };
	for (int & e : array)
	{
		e += 2;
		std::cout << e << std::endl;
	}
	return 0;
}

码例2:

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

对象的复制和移动

浅拷贝:将对象间数据元素一一对应复制。
深拷贝:被复制对象含有指针数据时,将指针所指对象进行复制,即创建新的内存空间给指针,而不是复制指针,避免复制指针和原指针指向同一内存导致析构时出错。
码例:(基于动态数组类)

语句 Arrayofpoints p2(p1);调用默认构造函数为浅拷贝,析构时两个指针析构同一内存会报错。
复制构造函数改为:
Arrayofpoints::Arrayofpoint(const Arrayofpoint&p)
{
size=p.size;
points=new Point[size];
for(int i=0;i<size;i++)
points[i]=p.points[i];
}

移动构造

C++11提供的新的构造方法,C++11之前只能复制。

  • 一般有可被利用的临时对象时,使用移动构造,将临时对象内容保留,销毁临时对象,不同于复制的两者均保留。
  • 语法:类名(类名 &&)
  • 左值是表达式结束后依然存在的持久对象。
  • 右值(&&)是即将消亡的值,函数返回的临时变量是右值。
    码例:
    说明:码例使用移动构造保存临时对象内容(函数返回值),避免使用深拷贝(返回指针类型)从而节省内存开销。
#include<iostream>
using namespace std;
class Intnum
{
private:
int *pointx;
public:
int getx(){return *pointx;}
Intnum(int x=0):pointx(new int(x))
{cout<<"calling constructer fun"<<endl;}
Intnum(Intnum &n):pointx(new int(*n.pointx))
{cout<<"calling copy constructer fun"<<endl;}
Intnum(Intnum &&n):pointx(n.pointx)
{n.pointx=nullptr;
cout<<"calling move constructer fun"<<endl;}
~Intnum(){ delete pointx;
cout<<"calling destructer fun"<<endl;}
};
Intnum getIntnum()
{Intnum a;
return a;}
int main()
{
cout<<getIntnum().getx()<<endl;
return 0;
}

&&n而不是&n,使得主函数中不用显式调用移动构造函数,返回值临时对象一产生就意味着找&&。

string 类

  • string 类常用的构造函数:
    (1)string(); //默认构造函数,建立长度为0的串,使用时长度自动扩展。
    例:string s1;
    (2)string(const char *s);//指针S指向的字符串常量初始化string对象。
    例:string s2 =“abc”;
    (3) string(const string &s);//复制构造函数
    例: string s3=s2;
  • 可以使用s[i] 访问串中下标为i的字符。
  • s<t 判断s是否小于t(按字典顺序比较)。
  • getline(cin,s1,’:’);//要包含string 头文件,输入整行数据,避免cin 遇到空格就结束问题。第三个参数是人为设定的分隔符,如果有两个getline(),输入数据时以设定分隔符为标志,结束第一个getline()进入第二个getline()。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值