1.面向对象(OO)的思想特点:
1.1.封装--将属性和方法结合成独立的单位,隐藏对象的内部细节;
1.2.抽象--相当于封装对外的接口;类--比如人
1.3.继承--子类对基类的属性和方法的继承;
1.4.多态--子类集成基类后,可以有不同的表现形式;比如动物的运动:狗四条腿跑;袋鼠两条腿跑。
2.数组名是数组的首地址
#include<iostream>
using namespace std;
int AddArray(int *array, int n);
int main()
{
int data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int size = sizeof(data) / sizeof(data[0]);
cout << "结果是:" << AddArray(data, size) << endl;
return 0;
}
int AddArray(int *array, int n)
{
int sum = 0;
int i;
for(i=0;i<n;i++)
{
sum += *array++;
}
return sum;
}
3.点操作符在C语言中用于结构中,而C++中的类相当于对结构和接口的封装;所以有:
cin.peek();
完整示例代码:
#include<iostream>
using namespace std;
int main()
{
int sum = 0;
cout << "请输入一串整数和任意数目的空格:";
int i; // C++允许在变量使用前的任意位置声明变量,C必须在开头
while (cin >> i) //输入操作符一次从cin中提取一个整数
{
sum += i;
while (cin.peek()==' ') //省略空格
{
cin.get(); //取出字符,并将指针指向下个字符
}
if (cin.peek()=='\n') //peek只查看,不取出
{
break;
}
}
cout << "结果是:" << sum << endl;
return 0;
}
4.检查用户输入是否输入预期内容,用switch、case:
注意:switch语句中表达式类型只能是整型或者字符型!!
注意:case里不要忘记添加break;
优势:与if语句相比,对于多条件判断,switch结构清晰,执行效率高。
缺点:不能判断区间。
// switch语句表达式为字符型
switch (typeIn)
{
case 'C':
case 'c':
tempOut = tempIn * RATIO + ADD_SUBTRACT;
typeOut = 'F';
typeIn = 'C';
break;
case 'F':
case 'f':
tempOut = (tempIn - ADD_SUBTRACT) / RATIO;
typeOut = 'C';
typeIn = 'F';
break;
default:
typeOut = 'E';
break;
}
//switch表达式为整型
int main()
{
int score = 0;
cout << "请打分: " ;
cin >> score;
switch(score)
{
case 10:
{
cout << "评价为经典" << endl;
break;
}
case 9:
{
cout << "评价为经典" << endl;
break;
}
case 8:
{
cout << "评价为非常好" << endl;
break;
}
case 7:
{
cout << "评价为非常好" << endl;
break;
}
case 6:
{
cout << "评价为一般" << endl;
break;
}
default:
cout << "评价为烂片" << endl;
break;
}
return 0;
}
5.函数的重载
重载目的:方便对不同数据类型进行相同的处理。
不要在程序中重载函数太多,同时作好注释。
函数的重载多和模板一块使用,方便程序的简化,只需将参数的类型模板化即可。
注意:重载函数需要均定义一遍,因为输入参数不同的重载函数本质上不是同一个函数!!
重载实例:
/*
C++函数重载实例,要旨如下:
1.函数名称一样;
2.函数的参数个数不一样,或者函数的参数类型不一样;
3.重载函数一样需要提前定义,虽然函数名称一样,但本质上不算同一个函数。
C++函数重载的作用:简化编程工作、提高代码可读性。
*/
#include<iostream>
void converTemperture(double tempIn, char typeIn);
void converTemperture(int tempIn, char typeIn); //函数重载的时候定义也要再写一次,因为函数输入不同,实际上相当于两个函数
int main()
{
double tempIn;
int tempInInt;
char typeIn;
std::cout << "请以【xx.x C】或者【xx.x F】格式输入一个温度:";
std::cin >> tempIn >> typeIn;
std::cin.ignore(100, '\n'); //清除键盘缓冲区
std::cout << "\n";
converTemperture(tempIn, typeIn);
std::cout << "请以【xx C】或者【xx F】格式输入一个温度:";
std::cin >> tempInInt >> typeIn;
std::cin.ignore(100, '\n'); //清除键盘缓冲区
std::cout << "\n";
converTemperture(tempInInt, typeIn);
return 0;
}
void converTemperture(double tempIn, char typeIn)
{
const unsigned short ADD_SUBTRACT = 32;
const double RATIO = 9.0 / 5.0;
double tempOut;
char typeOut;
switch (typeIn)
{
case 'C':
case 'c':
tempOut = tempIn * RATIO + ADD_SUBTRACT;
typeOut = 'F';
typeIn = 'C';
break;
case 'F':
case 'f':
tempOut = (tempIn - ADD_SUBTRACT) / RATIO;
typeOut = 'C';
typeIn = 'F';
break;
default:
typeOut = 'E';
break;
}
if(typeOut != 'E')
{
std::cout << tempIn << typeIn
<< " = " << tempOut
<< typeOut << "\n\n";
}
else
{
std::cout << "输入错误" << "\n\n";
}
std::cout << "请输入任意字符结束程序!" << "\n\n";
std::cin.get();
}
void converTemperture(int tempIn, char typeIn)
{
const unsigned short ADD_SUBTRACT = 32;
const double RATIO = 9.0 / 5.0;
int tempOut;
char typeOut;
switch (typeIn)
{
case 'C':
case 'c':
tempOut = tempIn * RATIO + ADD_SUBTRACT;
typeOut = 'F';
typeIn = 'C';
break;
case 'F':
case 'f':
tempOut = (tempIn - ADD_SUBTRACT) / RATIO;
typeOut = 'C';
typeIn = 'F';
break;
default:
typeOut = 'E';
break;
}
if(typeOut != 'E')
{
std::cout << tempIn << typeIn
<< " = " << tempOut
<< typeOut << "\n\n";
}
else
{
std::cout << "输入错误" << "\n\n";
}
std::cout << "请输入任意字符结束程序!" << "\n\n";
std::cin.get();
}
6.数据类型--数组、指针、结构
6.1数组的优点:可以把许多个同类型的值存储在同一个变量名下。
数组是由连续的内存位置组成的。
int aa[10]; //定义可存储10个整形数据的数组
#include<iostream>
#define ITEM 5 //方便修改数组的大小,不用去代码里一一修正
int main()
{
int num[ITEM];
std::cout << "请输入" << ITEM << "个整型数据!\n\n";
for(int i=0; i<ITEM; i++)
{
std::cout << "请输入第" << i+1 <<"个数据:";
while(!(std::cin >> num[i])) //检验数据输入准确性
{
std::cin.clear(); //清空数据输入缓冲区
std::cin.ignore(100, '\n');
std::cout << "请输入一个合法的值";
}
}
int total = 0; //局部变量需要初始化
for(int j=0; j<ITEM; j++)
{
total += num[j];
}
std::cout << "总和是:" << total << "\n";
std::cout << "平均值是:" << (float)total / ITEM;
return 0;
}
6.2 字符串数组:
std::string
#include<iostream>
#include<string>
int main()
{
std::string str;
std::cout << "请输入一个字符串:";
std::getline(std::cin, str); //getline可以获取所有的字符,std::in不行,会在空格出断掉
std::cout << str <<"\n";
return 0;
}
6.3 数组的数据类型可以是任意形式的,可以为指针形式的 ,也可以是对象形式的。
7.指针
指针的作用可以理解为走后门的媒介,别人通过指针走后门。
函数内部声明的变量为局部变量,作用域在这个函数内部,但指针可以在函数外部对该局部变量进行更改。
7.1对于变量索引的两种方法:
1)变量名;
2)地址。
7.2指针变量前边的类型是用来说明指针指向的数据的类型,请务必匹配使用。例如下面的代码,定义的指针*p是int型,则数据pp也得是int型。
int *p;
int pp = 132;
p = &pp;
7.3 允许void类型指针变量,指针只是存放地址
注意:对一个无类型指针进行解引用前,必须先把它转换为一种适当的数据类型
void *p;
7.4 *号的两种用途:
1)用于创建指针
a = 123;
int *pointer = &a;
2)对指针进行解引用
*pointer = 123;
8.数组和指针
8.1 如何用指针表示数组
指针表示数组的基地址,数组名就是基地址,如想索引数组中的后面的数字,则进行ptr1++。
注意:ptr1++并不是将地址值简单+1处理,而是按照指向的数组的数据类型来递增的,即+sizeof(int).
// 以下两句等同, myArray表示数组
int *ptr1 = &myArray[0];
int *ptr2 = myArray;
9.结构
结构是一种由程序员定义的,由其他变量类型组合而成的数据类型,定义格式如下
// 结构的基本语法
struct name
{
type varName1;
type varName2;
}; //注意不要忘了分号
当需要处理一些具有多种属性的数据时,结构往往是最好的选择,例如定义结构如下
// 统计成员的姓名、身份证和性别
struct FishOil
{
std::string name;
std::string uid;
char sex; //F==Female,M==Male
}
通过下面的语法创建该类型的变量,进行赋值操作
// 通过结构创建变量
FishOil Jiayu; //创建一个FileOil结构类型Jiayu
Jiayu.name = "小甲鱼";
Jiayu.uid = "123";
Jiayu.sex = 'M';
//若创建结构类型变量的时候已经知道各个成员相关的值,可以声明新变量的同时进行赋值
FishOil Jiayu = {"小甲鱼", "123", 'M'};
10.结构与指针
指针也可以指向结构,但索引成员变量时,不同于数组的ptr1++.
FishOil *pJiayu = &Jiayu; //创建指向结构变量Jiayu的指针
//因为指针的类型必须与指向的地址的变量类型一致,所以pJiayu指针的类型也是FishOil
访问结构变量的两种方法:
//方法一:通过指针解引用访问变量值
(*pJiayu).name = "黑夜"; //当成结构变量的时候用.
(*pJiayu).uid = "234";
(*pJiayu).sex = 'F';
//方法二:通过箭头 ->访问
pJiayu -> name = "黑夜"; //当成指针的时候用->
pJiayu -> uid = "234";
pJiayu -> sex = 'F';
std::cout << pJiayu -> name;
std::cout << pJiayu -> uid;
std::cout << pJiayu -> sex;
11.传值、传址和传引用
11.1 函数传值的局限性:无法对结果进行更新
//函数传值的局限性:无法对结果进行更改
//changeAge函数中更新的结果,不会反映到主函数的age中
//changeAge函数中的age和主函数中的age不是同一个
#include<iostream>
void changeAge(int age, int newAge);
int main()
{
int age = 24;
std::cout << "My age is " << age << "\n";
changeAge(age, age+1);
std::cout << "Now my age is " << age << "\n";
return 0;
}
void changeAge(int age,int newAge)
{
age = newAge;
std::cout << "In this, my age is " << age << "\n";
}
11.2解决办法
//方法一:传址
//函数传址的优势:可以对结果进行修正
#include<iostream>
void changeAge(int *age, int newAge); //用指针代替变量名
int main()
{
int age = 24;
std::cout << "My age is " << age << "\n";
changeAge(&age, age+1); //地址作为函数的输入
std::cout << "Now my age is " << age << "\n";
return 0;
}
void changeAge(int *age,int newAge)
{
*age = newAge;
std::cout << "In this, my age is " << *age << "\n";
}
//方法二:传引用
12.对象
12.1 对象和结构类似,区别是对象可以包含变量和函数,而结构只能有变量。
//类的定义
//函数的使用两要素:声明+定义,在类中声明,类后定义
#include<iostream>
class Car
{
public:
std::string color; //声明类的属性,参数
std::string engine;
float gas_tank;
unsigned int wheel;
void fill_tank(float liter); //在类中声明函数
}; //不要丢了分号
//函数(方法)的定义通常在类声明的后面
void Car::fill_tank(float liter) //::作用域解析符,fill_tank是属于Car类里面的
{
gas_tank += liter;
}
int main()
{
Car car1; //创建一个类的对象
car1.color = white;
car1.fill_tank(12);
return 0;
}
13.构造器和析构器
13.1 构造器
构造器是类中的一种特殊的方法,构造器的名字必须和所在类的名字一样,系统在创建某个类的实例时会自动调用该类的构造器,构造器不会返回任何值。
构造器的作用就是对创建的对象进行初始化,简化代码步骤,无须对创建的对象进行重复赋值。
创建构造器,需要先把它的声明添加到类里:
// 类中声明构造器
class Car{
Car(void); //声明构造器
};
// 类定义之后定义构造器本身
Car::Car(void) //定义构造器
{
color = "White";
engine = "V8";
wheel = 4;
gas_tank = FULL_GAS;
}
// 构造器的使用
int main()
{
Car car1; //创建对象时会自动调用构造器进行初始化
return 0;
}
构造对象数组
//构造对象数组
Car mycar[10];
//调用格式
mycar[1].running;
副本构造器:用于对象赋值(包含指针)
Myclass(const Myclass &rhs)
13.2 析构器
构造器用来完成事先的初始化和准备工作(申请分配内存),析构器用来完成事后的清理工作(清理内存)。
析构器和构造器有着一样的名字,只不过在前面多了一个波浪号“~”前缀。
class Car
{
Car(void);
~Car(); //析构器,前有波浪号,不带任何参数
};
14.类的继承
对现有的代码进行进一步的扩展,无须重新写基类中的属性和函数。
//继承的定义格式
class SubClass:public SuperClass{...} //注意时单冒号
//举例
class Pig:public Animal{...}
实例:
#include<iostream>
#include<string>
class Animal //定义基类
{
public:
std::string mouth;
void eat(); //声明基类方法
void sleep();
void drool();
};
class Pig : public Animal //定义子类,并扩展独有的属性方法
{
public:
void climb();
};
class Turtle : public Animal
{
public:
void swim();
};
void Animal::eat() //定义基类方法
{
std::cout << "I'm eatting!" << std::endl;
}
void Animal::sleep()
{
std::cout << "I'm sleeping!" << std::endl;
}
void Animal::drool()
{
std::cout << "I'm drooling!" << std::endl;
}
void Pig::climb() //定义子类方法
{
std::cout << "I'm climbing!" << std::endl;
}
void Turtle::swim()
{
std::cout << "I'm swimming!" << std::endl;
}
int main()
{
Turtle turtle;
Pig pig;
turtle.eat(); //子类对象可以调用基类的方法
turtle.swim(); //子类可以调用自己的方法
pig.eat();
pig.climb();
return 0;
}
15.继承中的构造器和析构器
子类创建对象时,先调用基类的构造器,然后再调用子类的构造器。基类必须在子类之前初始化。
构造器中包含参数的情况
//子类的构造器无任何实现
Animal::Animal(std::string theName){
name = theName;
}
Pig::Pig(std::string theName):Animal(theName){
}
/*当调用Pig pig("小猪");将把字符串"小猪"传递给pig()和Animal(),
赋值的实际动作将发生在Animal()方法里,因为子类构造器无实际动作。
*/
基类的析构器在子类的最后一条语句执行完毕后才被调用。主函数执行完毕后,执行子类析构器,再执行基类析构器。
16.访问控制
访问控制是用来保护类里的方法和属性的手段。
public //任何代码可访问
protected //类本身和子类中可调用
private //类本身可访问,子类不行
//注意:主函数中通过类创建对象,再通过对象访问,属于类外访问,
//也就是说,即使是类本身创建的对象,也无法访问其protected,private
17.覆盖和重载
17.1 覆盖
覆盖是类里重新声明该方法,要求方法名,输入参数一致,仅内部的实现方法不一样。
//子类方法的覆盖
class Animal //定义基类
{
public:
std::string mouth;
void eat(); //声明基类方法
void sleep();
void drool();
};
class Pig : public Animal //定义子类,并扩展独有的属性方法
{
public:
void climb();
void eat(); //将要覆盖的基类方法在子类中重新进行声明
};
//在子类的该方法中进行重新覆盖
void Pig::eat() //定义子类属性方法
{
Animal::eat(); //继承的原基类的方法
std::cout << "I'm eatting fish!" << std::endl; //子类中覆盖的方法
}
17.2 重载
重载可以定义多个同名的方法函数,但要求方法的输入参数不同。
详细内容见 5.函数的重载。
注意:区分重载和覆盖!!
注意:继承之后的方法函数不能进行重载!!
18.友元关系
解决的问题:一个完全无关的类由于某些特殊原因需要访问到某个protected成员,甚至是某个private成员的问题。
友元关系是类之间的一个特殊关系,这种关系不仅允许友元类访问对方的public方法和属性,还允许友元访问对方的protected和private方法和属性。
声明友元关系只需要在类声明的任意地方(可以在public、protected或private段落)加一条friend class **即可。
//友元实例
#include<iostream>
#include<string>
class Lovers
{
public:
Lovers(std::string theName); //声明构造函数
void kiss(Lovers *lover); //指针指向的是Lovers的对象,所以指针类型需为Lovers
void ask(Lovers *lover, std::string something);
protected:
std::string name;
friend class Others; //声明友元
};
class Boyfriend : public Lovers
{
public:
Boyfriend(std::string theName);
};
class Girlfriend : public Lovers
{
public:
Girlfriend(std::string theName);
};
class Others
{
public:
Others(std::string theName);
void kiss(Lovers *lover);
protected:
std::string name;
};
Lovers::Lovers(std::string theName) //定义构造函数
{
name = theName;
}
void Lovers::kiss(Lovers *lover)
{
std::cout << name << "亲亲我家的" << lover ->name << std::endl;
}
void Lovers::ask(Lovers *lover, std::string something)
{
std::cout << "宝贝" << lover ->name << "帮我" << something << std::endl;
}
Boyfriend::Boyfriend(std::string theName) :Lovers(theName) //定义子类的构造函数
{
}
Girlfriend::Girlfriend(std::string theName) :Lovers(theName) //定义子类的构造函数
{
}
Others::Others(std::string theName)
{
name = theName;
}
void Others::kiss(Lovers *lover)
{
std::cout << name << "亲一下" << lover ->name <<std::endl;
}
int main()
{
Boyfriend boyfriend("A君");
Girlfriend girlfriend("B妞");
Others others("路人甲");
girlfriend.kiss(&boyfriend);
girlfriend.ask(&boyfriend, "洗衣服啦");
std::cout << "\n铛铛铛铛!路人甲登场。。。。。\n";
others.kiss(&girlfriend);
return 0;
}
19.静态属性和静态方法
非必要不全局变量!!因为程序中的任何地方都可以修改这个全局变量,不容易查找漏洞。
当仅需要在创建或删除对象的时候访问计数器,则可以使用静态属性或静态函数。
静态属性和静态方法属于该类,可以在没有创建任何对象的情况下调用有关方法。
注意:无法在静态方法里访问非静态成员!!因为静态方法由全体对象共享
静态化可以让有关的数据仍在该类的所有对象间共享。
定义方法:在声明前添加static关键字。
//静态属性、静态方法实例
#include<iostream>
#include<string>
class Pet
{
private:
static int count; //声明静态属性,仅类内部通过构造函数可修改该值
protected:
std::string name;
public:
Pet(std::string theName);
~Pet();
static int getCount(); //声明静态方法,提供外部读取接口
};
class Dog : public Pet
{
public:
Dog(std::string theName);
};
class Cat : public Pet
{
public:
Cat(std::string theName);
};
//在类声明的外部进行声明
int Pet::count = 0; //作用1:开辟存储空间;作用2:初始化为0
Pet::Pet(std::string theName)
{
name = theName;
count++;
std::cout << "一只宠物出生了,名字叫做: " << name << "\n";
}
Pet::~Pet()
{
count--;
std::cout << name << "挂掉了\n";
}
int Pet::getCount()
{
return count;
}
Dog::Dog(std::string theName) : Pet(theName)
{
}
Cat::Cat(std::string theName) : Pet(theName)
{
}
int main()
{
Dog dog("Tom");
Cat cat("Jerry");
//静态方法使用ClassName::methodName()调用,不要用objectName.methodName()调用!!
std::cout << "\n已经诞生了" << Pet::getCount() << "只宠物!\n\n";
{ //花括号的用处是限定作用范围,当花括号运行完毕时,执行析构函数
Dog dog_2("Tom_2");
Cat cat_2("Jerry_2");
std::cout << "\n现在呢,已经诞生了" << Pet::getCount() << "只宠物!\n\n";
}
std::cout << "\n现在还剩下 " << Pet::getCount() << "只宠物!\n\n";
return 0;
}
20.虚方法
1.引出目的:为了解决让C++调用运行时定义的类型而不是编译时调用的类型,即按照程序设计的预期工作。
//虚方法引入的必要性
#include<iostream>
#include<string>
class Animal //定义基类
{
public:
void eat(); //声明基类方法
// virtual void eat();
};
class Pig : public Animal //定义子类,并扩展独有的属性方法
{
public:
void eat(); //方法继承覆盖
};
void Animal::eat() //定义基类方法
{
std::cout << "I'm eatting!" << std::endl;
}
void Pig::eat() //定义子类属性方法
{
Animal::eat();
std::cout << "I'm eatting pig!" << std::endl;
}
int main()
{
/*
此处,C++编译时会根据指针类型选为Animal的成员方法,而我们想要的是
C++会根据后面的new Pig,生成Pig类型的指针
*/
Animal *pig = new Pig;
pig ->eat();
return 0;
}
/*
//虚方法的作用实例
virtual void eat(); //声明基类方法,仅需前加virtual
*/
运行结果:
I'm eatting!
I'm eatting!
2.解决办法:
仅虚在重覆盖的基类方法声明前加上virtual
virtual void eat();
运行结果:
I'm eatting!
I'm eatting fish!
注意:虚方法是继承的,在基类中声明为虚方法,在子类中就不能再声明为非虚方法了!!
基类中的析构函数也是虚函数,子类会自动继承基类的虚函数。
21.抽象方法
在声明宠物玩的方法时候,不存在什么都能玩的情况,仅仅是为了继承使用,所以可以设置为抽象方法。抽象方法的使用仅需在声明虚方法的基础上,在原型末尾加上“=0”(编译器无须寻找该方法的实现,仅在继承的时候才考虑它的实现)
class Animal //定义基类
{
public:
virtual void eat() = 0; //抽象方法的声明
};
//下面关于基类该抽象方法的实现可以删去,没有必要了
// void Animal::eat() //定义基类方法
// {
// std::cout << "I'm eatting!" << std::endl;
// }
疑问:既然基类中仅设置一个空声明不用写实现,那直接在子类中以扩展的方式声明实现不就好了,为什么还要多次一举??
22.多态性
一个接口,多种方法!
1.多态实现绑定的两种方法:
1)编译时实现:通过重载 --运行速度快
2)运行时实现:通过虚函数 --高度灵活和抽象
23.运算符重载
函数类型 operator 运算符名称(形参列表)
{
对运算符的重载处理
}
重载中,函数类型最好是用它函数的类型,即它的类。
//对运算符“+”进行重载实例
#include<iostream>
class Complex
{
public:
Complex(); //无参构造函数声明
Complex(double r, double i); //有参构造函数声明
Complex operator+(Complex &d); //重载函数类型写成函数的类型,也即函数所在的类Complex
void print();
private:
double real;
double imag;
};
Complex::Complex() //定义无参构造函数
{
real = 0;
imag = 0;
}
Complex::Complex(double r, double i) //定义有参构造函数
{
real = r;
imag = i;
}
//函数类型 函数作用域::operator 运算符(形参)
Complex Complex :: operator+(Complex &d) //定义运算符重载函数
{
Complex c; //创建实例对象
c.real = real + d.real;
c.imag = imag + d.imag;
return c;
}
void Complex::print()
{
std::cout << "(" << real << "," << imag << "i)\n";
}
int main()
{
Complex c1(3,4), c2(5,-10), c3; //创建三个对象,通过构造函数初始化
c3 = c1 + c2;
std::cout << "c1 = ";
c1.print();
std::cout << "c2 = ";
c3.print();
std::cout << "c1 + c2 = ";
c3.print();
return 0;
}
1.以下五个运算符不允许重载,此外均可
.(成员访问运算符)
.*(成员指针访问运算符)
::(域运算符)
sizeof(尺寸运算符)
?:(条件运算符)
2.C++不允许自定义新的运算符,只能在现有的基础上进行重载。
3.运算符重载规则
运算符重载不能改变运算符运算对象(操作数)个数。
不能改变运算符的优先级别。
不能改变运算符的结合性。
24.操作符重载
1.为什么要以普通函数的形式在类外进行定义?为什么不能像上面的加运算符重载一样在类里实现??以普通函数的形式为什么可以重载<<操作符??
在类中重载的操作符,作用域仅在该类中??
运算符重载operator前无&,操作符重载operator前有&?(好像是因为下面两个是以地址为介质处理数据的,所以用取地址&)
//对<<操作符进行重载
//类里声明友元函数
class A
{
public:
private:
int x;
int y;
friend std::ostream& operator<<(std::ostream& os, A a);
};
//类外声明该普通函数
std::ostream& operator<<(std::ostream& os, A a);
//普通函数定义
std::ostream& operator<<(std::ostream& os, A a)
{
os << a.x << "/" << a.y;
return os;
}
//对 = 操作符进行重载
Myclass &operator = (const Myclass &rhs);
25.assert函数
//引用的头文件
#include<cassert>
//A为真,啥也不做;A为假,做某事.
assert(A);
26.动态内存
1.动态内存由没有名字、只有地址的内存块构成,是在程序运行期间动态分配的。
2.使用new语句动态分配内存,new语句将返回新分配地址块的起始地址。
3.用完内存块之后,应及时用delete语句删除内存块;同时将与之关联的指针设置为NULL。
4.new返回的地址块可能有先前的东西,所以最好是先覆盖再访问,或者直接在类中的构造器进行初始化。
5.动态内存块没有作用域,但是用来保存其地址的指针变量是受作用域影响的。
Company *company = new Company("Apple"); //创建动态内存
company -> printInfo();
delete company; //删除动态内存
company = NULL; //删除动态内存对应的地址
//company对应的地址已经被删除,所以下面可以再次使用该指针
company = new TechCompany("Apple", "Iphone");
company -> printInfo();
delete company;
company = NULL;
27.动态数组
1.新建一个动态数组
int *x = new int[10];
x[1] = 45;
x[2] = 8;
//或者变量保存数组元素个数
int count = 10;
int *x = new int[count];
2.删除一个动态数组
delete []x;
28.函数模板
template <class T> //<class T>表示T在接下来的函数里代表一种不确定的数据类型
//class不代表类,仅仅是一种表示
void foo(T param)
{
do something
}
实例:
//函数模板--交换
#include<iostream>
#include<string>
//template <typename T>也可
template <class T>
void swap(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
int main()
{
int i1 = 100;
int i2 = 200;
std::cout << "交换前,i1 = " << i1 << ", i2 = " << i2 << "\n";
swap(i1, i2);
std::cout << "交换后,i1 = " << i1 << ", i2 = " << i2 << "\n";
std::string s1 = "小甲鱼";
std::string s2 = "加菲猫";
std::cout << "交换前,s1 = " << s1 << ", s2 = " << s2 << "\n";
swap(s1, s2);
std::cout << "交换后,s1 = " << s1 << ", s2 = " << s2 << "\n";
return 0;
}
29.类模板
#include<iostream>
#include<string>
template <class T>
class Stack
{
public:
Stack(unsigned int size = 100);
~Stack();
void push(T value);
T pop();
private:
unsigned int size;
unsigned int sp;
T *data; //栈指针
};
template <class T>
Stack<T>::Stack(unsigned int size)
{
this -> size = size;
data = new T[size];
sp = 0;
}
template <class T>
Stack<T>::~Stack()
{
delete []data;
}
template <class T>
void Stack<T>::push(T value)
{
data[sp++] = value;
}
template <class T>
T Stack<T>::pop()
{
return data[--sp];
}
int main()
{
Stack<int> intStack(100);
intStack.push(1);
intStack.push(2);
intStack.push(3);
std::cout << intStack.pop() << "\n";
std::cout << intStack.pop() << "\n";
std::cout << intStack.pop() << "\n";
return 0;
}
30.容器vector
vector的容量是无限的,根据存入的数据动态扩容。
#include<iostream>
#include<string>
#include<vector>
int main()
{
std::vector<std::string> names;
names.push_back("小甲鱼");
names.push_back("小鱿鱼");
for(int i=0; i<names.size(); i++)
{
std::cout << names[i] << "\n";
}
return 0;
}
31.迭代器
迭代器是个智能指针,具有遍历复杂数据结构的能力。
作用是用来遍历容器,而且是通用的遍历容器元素的方式,无论容器是基于什么数据结构实现的,尽管不同的数据结构,遍历元素的方式不一样,但是用迭代器遍历不同容器的代码是完全一样的。
//解决的问题:适用于所有容器,各容器方式不同
#include<iostream>
#include<string>
#include<vector>
int main()
{
std::vector<std::string> names;
names.push_back("小甲鱼");
names.push_back("小鱿鱼");
std::vector<std::string>::iterator iter = names.begin();
while(iter != names.end())
{
std::cout << *iter << "\n";
++iter;
}
return 0;
}