1.指针函数(是一个函数)
一个函数可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。
即定义的时候用指针进行定义,这样表示返回值是一个指针变量,返回一个地址
案例如下:
#include<iostream>
using namespace std;
int *newInt(int value);
//声明一个指针函数,返回一个地址!!
int *newInt(int value)
{
int *myInt = new int;
*myInt = value;
return myInt;
}
int main()
{
//新建一个整型变量空间,并将x指针指向这个空间
int *x = newInt(20);
cout << *x;
delete x;
x = NULL;
return 0;
}
2.函数指针(是一个指针变量)
指向函数首地址的指针变量(存储函数首地址),指向我们可以理解为存储
如:int (*p)()这是一个指向函数的指针变量。
案例如下:
#include <stdio.h>
int fun(int x,int y);
int fun(int x,int y)
{
int z;
z = (x > y)? x:y;
return z;
}
int main()
{
int i,a,b;
//声明函数指针
//函数如何声明参数【类型、个数】那么函数指针就应该如何声明参数,否则会出错。
int (*p)(int,int);/* int(*p)()也是一样的 */
p = fun;//给函数指针p赋值,使其指向函数fun,函数名就是首地址!这里存储的相当于函数的地址,然后通过*p调用函数
printf("请输入10个数字:\n");
scanf("%d", &a);
for(i = 0; i< 9; i++)
{
scanf("%d", &b);
a = (*p)(a,b);//通过指针p调用函数fun,函数正常返回int型
}
printf("The Max number is %d.", a);
return 0;
}
3.静态对象强制类型转换
Company是一个基类,TechCompany是一个子类,(两者是继承关系,我们知道可以转换)我们需要将从Company生成的实例,初始化了TechCompany的实例,因此需要通过类型转换强制转换成TechCompany。
如下:
/*静态对象强制类型转换*/
#include <iostream>
using namespace std;
#include <string>
class Company
{
public:
//构造器
Company(string theName,string product);
//方法
virtual void printInfor();//打印信息
protected:
string name;
string product;
};
//基类构造器实现
Company::Company(string theName,string product)
{
name = theName;
this->product = product;
}
//基类方法实现
void Company::printInfor()
{
cout << "这家公司的名字叫:" << name << "正在生产" << product << endl;
}
class TechCompany:public Company
{
public:
//构造器
TechCompany(string theName, string product);
//方法
void printInfor();//打印信息
};
//构造器实现
TechCompany::TechCompany(string theName, string product):
Company(theName, product)
{
}
//方法实现
void TechCompany::printInfor()
{
cout << name << "公司大量生产了" << product << "\n\n";
}
int main()
{
Company *company = new TechCompany("苹果", "IPHONE");
//将Company类型强制转换为TechCompany类型
/*不能这样:TechCompany *techCompany = company;因为两个对象不同*/
TechCompany *techCompany = (TechCompany*)company;
techCompany->printInfor();
//释放内存(此处company与techCompany两个指针都指向TechCompany定义的对象)
//所以释放内存只需要释放一次即可[注意不能停同时删除这两个!!!]
/*不能重复释放啊*/
delete company;//删除指针地址
company = NULL;//为指针指向的地址赋空值
techCompany = NULL;
return 0;
}
记得删除申请的内存块后,需要把两个指针变量都置为NULL;
4.动态对象强制类型转换
如果当出现不能转的情况,我们强行转就会出现错误,所以需要采用动态转换,如果转换失败 就会返回NULL,这样我们可以进行条件判断,如果转换失败了,我们就能做到及时止损。
如:这样转很明显是错的。因为两个实例毛关系都没有,转换不过来(开辟的是Company这个类的空间。不是TechCompany)因此需要一个动态转换。
Company *company = new Company("苹果", "IPHONE");
TechCompany *techCompany = TechCompany*(company);
正确全体代码如下:
/*动态对象强制类型转换*/
/*万一强制转换的类型和目标类型结构完全不同,这个时候我们就需要采取高级的强制类型转换操作符了!*/
#include <iostream>
using namespace std;
#include <string>
class Company
{
public:
//构造器
Company(string theName,string product);
//方法
virtual void printInfor();//打印信息
protected:
string name;
string product;
};
//基类构造器实现
Company::Company(string theName,string product)
{
name = theName;
this->product = product;
}
//基类方法实现
void Company::printInfor()
{
cout << "这家公司的名字叫:" << name << "正在生产" << product << endl;
}
class TechCompany:public Company
{
public:
//构造器
TechCompany(string theName, string product);
//方法
void printInfor();//打印信息
};
//构造器实现
TechCompany::TechCompany(string theName, string product):
Company(theName, product)
{
}
//方法实现
void TechCompany::printInfor()
{
cout << name << "公司大量生产了" << product << "\n\n";
}
int main()
{
Company *company = new Company("苹果", "IPHONE");
//Company和TechCompany虽然结构相同,但是属于两个不同对象
/*先在两个尖括号之间写出想要转换的指针类型,然后将被转换的值写在括号里*/
TechCompany *techCompany = dynamic_cast<TechCompany*>(company);
/*如果转换失败就会返回NULL*/
if(techCompany == NULL)
{
cout << "悲催!!!" << endl;
}
else
cout << "成功!!!" << endl;
//释放内存(此处company与techCompany两个指针都指向TechCompany定义的对象)
//所以释放内存只需要释放一次即可[注意不能停同时删除这两个!!!]
delete company;//删除指针地址
techCompany = NULL;
company = NULL;//为指针指向的地址赋空值
return 0;
}
5.避免内存泄露
正确的做法:建立一个指针变量,指向整型数组的首地址(1000个整型),然后进行删除释放,并将x置于NULL。+
int *x;
x = new int[1000];
delete[]x;
x = NULL;
错误1: 会导致内存泄露,几个new必须对应几个delete
x = new int[1000];
x = new int[4000];
delete[]x;
x = NULL;
错误2:函数里面申请空间,出来后会x被消除(指针变量x超过作用域)。那么就只剩下一个申请的地址,但是没有对应的指针了
void foo()
{
My Class *x;
x = new MyClass();
}
解决方法
1.设定返回值为这个指针变量,即返回一个地址。
2.在函数里面就进行delete 。
6.容器与算法
能容纳两个或更多值的数据通常我们称之为容器,数组是C++唯一直接支持的容器,但数组并不适合来解决所有的问题。
数组这种数据结构最大的先天不足就是它受限于一个固定的长度。C++标准库提供的向量(vector)类型从根本上解决了数组先天不足的问题(内存固定,如果不用那么多内存编译器也会为其分配),我们用不着对一个向量能容纳多少元素做出限定,因为向量可以动态地随着你往它里面添加元素而无限增大。
- size()方法查知某给定向量的当前长度(即包含的元素个数);
- 可以根据访问数组元素的语法来访问给定向量里的各个元素,即下标访问法;
- push_back()方法往它里面添加东西;
- begin()方法开始数据的位置;
- end()方法表示最后元素的下一个位置,不是最后元素的位置。
- 创建容器的形式:vector<type> vectorName
案例1:容器就像是一个可以不断扩充和收缩的数组
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<string> names;
names.push_back("刘舸");
names.push_back("侯悦");
names.push_back("刘小飞");
for(int i = 0; i < names.size(); i++)
{
cout << names[i] <<endl;
}
names[2] = "lg";/*还未添加所以不行*/
cout << names[2] << endl;
return 0;
}
案例二:
迭代器(只能指针,这是个指针奥!)
在遍历向量里的各个元素时,我们仍然把其视为C++数组来看待,刚好我们的向量容器允许下标访问names[],但如果改用另一种不提供此方法访问的容器(比如栈),我们就需要做很多修改才能实现。对元素进行遍历是一种十分常见的任务,所以应该有一种标准的方式来做这件事情
iterator(迭代器)就是这么来的是一种复杂指针,可以进行遍历数据。使用迭代器,修改容器时就不需要改很多代码了:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<string> names;
names.push_back("刘舸");
names.push_back("hy");
vector<string>::iterator iter = names.begin();/*返回初始位值的值,迭代器是智能指针哦*/
while(iter != names.end())/*end取最后一个元素的下一个位置,表示底部*/
{
cout << *iter << endl;
++iter;
}
return 0;
}
案例3:算法的魅力
/*C++标准库里也有算法*/
#include<iostream>
#include <string>
#include <vector>//向量
#include <algorithm>//算法
int main()
{
//定义向量
std::vector<std::string> names;
//往容器中存放数据
names.push_back("Larry");
names.push_back("Rola");
names.push_back("Dingding");
names.push_back("Laary");
names.push_back("TAOM");
names.push_back("jerry");
//sort()算法按照英文字母排序
std::sort(names.begin(), names.end());/*容器里的直接排列*/
//迭代器;names.begin()开始位置,names.end()表示最后元素的下一个位置
std::vector<std::string>::iterator iter = names.begin();
while(iter != names.end())//names.end()表示最后元素的下一个
{
//迭代器是智能指针,可以使用*
std::cout << *iter << "\n";
++iter;
}
return 0;
}