小甲鱼C++快速入门学习笔记_小甲鱼c++快速入门笔记,2024年最新2024年物联网嵌入式开发面试题

7 阅读21分钟

img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

    {
case 1:
std::cin >> tempIn;
tempOut = calc(tempIn);
break;
case 2:
std::cin >> tempIn >> tempIn2;
tempOut = calc(tempIn, tempIn2);
break;
case 3:
std::cin >> tempIn >> tempIn2 >> tempIn3;
tempOut = calc(tempIn, tempIn2, tempIn3);
break;
default:
std::cout << "错误!";
break; 
}
std::cout << "计算结果为:" << tempOut << "\n";

std::cin.ignore(100, '\n');
std::cout << "\n";

    return 0;
}

double calc(double tempIn)
{
return tempIn*tempIn;
}

double calc(double tempIn, double tempIn2)
{
return tempIn*tempIn2;
}

double calc(double tempIn, double tempIn2, double tempIn3)
{
return (tempIn + tempIn2 + tempIn3);
}

第七讲:复杂的数据类型

数组:可以把许多个同类型的值存储在同一变量名下

实例1:输入的数据存储到数组中,并计算其和与平均值输出

#include

using namespace std;//使用作用域 

int main()
{
int array[10];
int i;
int sum=0;
double average;

cout << "请输入10个整数!\n"; 
for(i=0;i<10;i++)
{
cout << "请输入第" << i+1 << "个整数:";
cin >> array[i];
sum+=array[i];
}
average = sum/10.0; 
cout << "和为:" << sum << "\n" << "平均值为:" << average << "\n";  

return 0;
}

实例2:打印出用户输入的字符串

#include
#include

using namespace std;

int main()
{
string str;
cout << "请输入一个字符串:";
getline(cin, str);//getline为获取一行数据  cin >> str使用时遇到空格便默认输入接收 
cout << str;

return 0; 

实例3:实例1的规范改进

#include
//未完善 
using namespace std;//使用作用域 
//当输入错误时,cin会返回错误0 
#define ITEM 10
int main()
{
int array[ITEM];
int i;
int sum=0;
double average;

cout << "请输入" << ITEM << "个整数!\n"; 
for(i=0;i<ITEM;i++)
{
cout << "请输入第" << i+1 << "个整数:";
while(!(cin >> array[i])) //输入非法时是下一次输入时才进入循环 
{
cin.clear();//清楚不规范输入值 
cin.ignore(100,'\n');
cout << "\n请输入一个合法的值!";
cout << "\n请重新输入第" << i+1 << "个整数:\n"; 
cin >> array[i];
}
sum+=array[i];
}
average = (double)sum/ITEM; 
cout << "和为:" << sum << "\n" << "平均值为:" << average << "\n";  

return 0;
}

第八讲:复杂的数据类型——指针

小知识:程序以文件的形式存储在硬盘,但它们却是在计算机的内存中运行

                对于变量可以通过变量名与地址两种方式进行索引,变量的地址在程序执行期间是不会发生变换的

                地址是计算机内存中的某个位置;指针是专门用来存放地址的特殊类型变量

第九讲:复杂的数据类型——指针02

                指针的类型必须与由它保存其地址的变量的类型一致,当某个变量的地址给了指针p时,就可以通过*p(即表示该地址的数据)来对该变量数据进行操作

                一定要牢记一个事实:指针所保存的是内存中的一个地址,它并不保存指向的数据的值本身。因此务必确保指针对应一个已经存在的变量或者一块已经分配的内存            

               *有两种用途,一是创建指针: int *p = &a;   另外是解引用  *p = 123;

                c++允许多个指针指向同一个地址

               .void *则为“无类型指针”,可以指向任何数据类型。对一个无类型指针进行解引用前必须先将他转换为一个适当的数据类型。

实例1:通过指针形式修改变量值(指针的解引用)

#include
using namespace std;

int main()
{
int a = 123;
float b = 3.14;
char c = 'c';
unsigned long d = 19880808;
string e = "I love FishC!";

cout << "a的值是:" << a << "\n";
cout << "b的值是:" << b << "\n";
cout << "c的值是:" << c << "\n";
cout << "d的值是:" << d << "\n";
cout << "e的值是:" << e << "\n\n";

int *aPointer = &a;
float *bPointer = &b;
char *cPointer = &c;
unsigned long *dPointer = &d;
string *ePointer = &e;

*aPointer = 456;
*bPointer = 4.13;
*cPointer = 'F';
*dPointer = 20190525;
*ePointer = "I love Beauty!";

cout << "a的值是:" << a << "\n";
cout << "b的值是:" << b << "\n";
cout << "c的值是:" << c << "\n";
cout << "d的值是:" << d << "\n";
cout << "e的值是:" << e << "\n\n";

}

第十讲:复杂的数据类型——指针和数组

计算机是把数组以一组连续的内存块保存的。

数组的第一个元素的地址为该数组的基地址。

实例1:数组元素地址打印

#include

using namespace std;

int main()
{
const unsigned short ITEMS = 5;
int intArray[ITEMS] = {1,2,3,4,5};
char charArray[ITEMS] = {'F','i','s','h','C'};

int *intPtr = intArray;
char *charPtr = charArray;

cout << "整型数组输出:" << '\n';
for(int i=0;i < ITEMS; i++)
{
std::cout << *intPtr << " at " << (intPtr) << '\n';//用reinterpret_cast把指针类型强制转换为unsigned int
intPtr++;//地址加上以其所定义的类型所占的字节 


cout << "字符型数组输出:" << '\n';
for(int i=0;i < ITEMS; i++) 
{
cout << *charPtr << " at " << (charPtr) << '\n';
charPtr++;
}
return 0;

实例2:数组重载

#include

using namespace std;

void print(int *pBegin,int *pEnd)
{
while(pBegin != pEnd)
{
cout << *pBegin;
++pBegin;//地址加1 
}
}

void print(char *pBegin,char *pEnd)
{
while(pBegin != pEnd)
{
cout << *pBegin;
++pBegin;//地址加1 
}

int main()
{
int num[5] = {0,1,2,3,4};
char name[5] = {'F','i','s','h','C'};

print(num,num + 5);
cout << '\n';
print(name,name + 5);
cout << '\n';

return 0;
}

实例3:泛型数组重载

#include

using namespace std;

template //将输入参数typename的类型赋值给elemType
void print(elemType *pBegin,elemType *pEnd)
{
while(pBegin != pEnd)
{
cout << *pBegin;
++pBegin;//地址加1 
}
}

int main()
{
int num[5] = {0,1,2,3,4};
char name[5] = {'F','i','s','h','C'};

print(num,num + 5);
cout << '\n';
print(name,name + 5);
cout << '\n';

return 0;
}

第十一讲:结构

结构是一种由程序员自己定义的、由其他变量类型组合而成的数据类型。其所能包含的变量的个数是没有限制的。

实例1:简单数据库读写

#include
#include  //文件操作 
#include <windows.h>        // 为了使用Sleep()函数 

struct FishOil
{
std::string name;
std::string uid;
char sex;
}; 

bool InitFishC();
bool ReadFishC(); 
void RecordFishC();
bool WriteFishC(FishOil *OilData);

int main()
{
int i;

InitFishC();  // 初始化数据。 
while( 1 )

std::cout << "请选择需要进行的操作: \n";
std::cout << "1. 打印数据到屏幕\n";
std::cout << "2. 录入数据\n"; 
std::cout << "3. 退出程序\n"; 
std::cin >> i;

switch( i )
{
case 1: 
if( ReadFishC() )
std::cout << "成功读取文件^_^\n\n";
else
std::cout << "读取文件失败T_T\n\n";
break;
case 2:
RecordFishC();
break;
case 3:
return 0;        
}
}

return 0;

}

bool InitFishC()
{
FishOil OilInit = {"小甲鱼", "fishc00001", 'M'};

        if( WriteFishC(&OilInit) == false )
std::cout << "初始化失败T_T\n";  

bool ReadFishC() //读文件 
{
std::string temp;
std::ifstream fileInput("FishC.txt");

if(  fileInput.is_open() )
{
std::cout << "\n正在输出记录数据...... ";
for( int i=0; i <= 100; i++ )      // 打印百分比 
{
std::cout.width(3);
std::cout << i << "%";
Sleep(50);
std::cout << "\b\b\b\b";
}
std::cout << "\n\n";

                std::cout << " 姓名 " << "  身份证  " << " 性别 " << "\n\n";

while( std::getline( fileInput, temp ) )
{
std::cout << temp << "   ";
std::cout << "\n";
}
std::cout << "\n\n";

return true;

else
return false;
}

void RecordFishC()//录入数据 
{
char goon, Save;
FishOil OilData;
FishOil *pOilData;

goon = 'Y';
while( 'Y' == goon )
{
std::cout << "请输入鱼C账号: ";
std::cin >> OilData.name;
std::cout << "请输入鱼C身份证:";
std::cin >> OilData.uid;
std::cout << "请输入性别:";
std::cin >> OilData.sex;

std::cout << "录入成功, 请问需要保存吗?(Y/N)";
std::cin >> Save;         
if( 'Y' == Save )
{
pOilData = &OilData;
if( WriteFishC( pOilData ) )
std::cout << "成功写入文件^_^\n";
else
std::cout << "写入文件失败T_T\n";        
}
else
{
return;
}

std::cout << "/n请问需要再次录入吗?(Y/N)";
std::cin >> goon; 
}
}

bool WriteFishC( FishOil *pOilData )//写文件 
{
std::ofstream fileOutput("FishC.txt", std::ios::app);  
// std::ios::app用来说明在老数据追加新数据 
if( fileOutput.is_open() )
{
fileOutput << pOilData->name << " ";
fileOutput << pOilData->uid << " ";
fileOutput << pOilData->sex << "\n";

fileOutput.close();
std::cout << "数据成功保存到FishC.txt\n\n";
return true; 

else
std::cout << "保存失败T_T\n";
return false;         
}

第十二讲:传值、传址和传引用

实例1:值传递

#include

void changeAge(int age,int newAge); 
int main()
{
int age = 24;//定义一个age,占一处地址 
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,占另一处地址 
{
age = newAge;
std::cout << "In this, my age is " << age << "\n"; 
}

绕开“值传递”问题的第一种方法是向函数传递变量的地址取代他的值。

实例2:指针地址传递

#include

void changeAge(int *age,int newAge); 
int main()
{
int age = 24;//定义一个age,占一处地址 
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,占另一处地址 
{
*age = newAge;
std::cout << "In this, my age is " << *age << "\n"; 
}

实例3:两值互换

#include

void swap(int *x,int *y);
int main()
{
int x,y;

std::cout << "请输入两个不同的值:";
std::cin >> x >> y;

swap(&x,&y);

std::cout << "调换后输出:" << x << ' ' << y <<"\n\n"; 

}

void swap(int *x,int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}

实例4:两值互换2

#include

void swap(int *x,int *y);
int main()
{
int x,y;

std::cout << "请输入两个不同的值:";
std::cin >> x >> y;

swap(&x,&y);

std::cout << "调换后输出:" << x << ' ' << y <<"\n\n"; 

}

void swap(int *x,int *y)
{
*x ^= *y;
*y = *x;
*x ^= *y;
}

实例5:不用指针的两值交换

#include  

void swap(int &x,int &y);

int main()
{
int x,y;
std::cout << "请输入两个不同的值:";
std::cin >> x >> y;

swap(x,y);
std::cout << "调换后输出:" << x << ' ' << y << "\n\n";

return 0;
}

void swap(int &x,int &y)
{
int temp;
temp = x;
x = y;
y = temp;
}

第十三讲:联合、枚举和类型别名

联合(union)与结构有很多相似之处,联合也可以容纳多种不同类型的值,但是它每次只能储存这些值中的一个(即当我们已经给联合里的一个成员赋值后,再给另一个成员赋值时,将丢弃第一个成员的值)

实例1:联合的应用

#include

union mima
{
unsigned long birthday;
unsigned short ssn;
const char *pet;//将pet声明为一个指向不变字符串的指针     
};

int main()
{
mima mima_1;

mima_1.birthday = 19970328;
std::cout << mima_1.birthday << "\n";

mima_1.pet = "Chaozai";//为字符串指针内的数据赋值 
std::cout << mima_1.pet << "\n";
std::cout << mima_1.birthday << "\n";//由于覆盖,将输出pet中字符串存储的地址 

return 0;
}

枚举(enum)类型用来创建一个可取值列表。枚举值不是字符串,编译器会按照各个枚举值在定义时出现的先后顺序把它们与0~n-1的整数(n是枚举值的总个数)分别关联起来。

实例2:枚举的应用

#include

int main()
{
enum weekdays{Monday, Tuesday, Wednesday, Thursday, Friday};//编译后依次变为0、1、2、3、4 

weekdays today;

today = Monday;
std::cout << today << "\n";

today = Friday;
std::cout << today << "\n";

switch (today)
{
case Monday:
std::cout << "hello";
break;
case Tuesday:
std::cout << "yiwofeiye";
break;
default:
std::cout << "Goodbye";
break;
}
return 0; 

类型别名(typedef)可以为一个类型定义创建一个别名。例如:

定义typedef int* intPointer;后便可以使用intPointer myPointer;来定义整型指针。

第十四节:对象

对象本质上不过是一种新的数据类型。

类(class)是一个模型(就像是一张蓝图,它决定一个对象将拥有什么样的属性、功能等),且每个类跟变量一样都有一个名字。当我们为这个类创建实例的时候,也就是对象(类的具体化实现)。

类由变量(类里的变量称属性)和函数(类里的函数称方法)组成,对象将使用那些变量来存放信息,调用那些函数来完成操作。同理对象内部有变量和函数,而结构通常只由各种变量构成。

例如std::cout即为使用的是std类中的cout对象;std:string数据类型使用的是std类中的string对象

 

面相对象编程技术(object-oriented programming)可以说是面向过程技术(procedural programming)的替代品。

面向过程技术关注的是对数据进行处理的过程,面向对象(OOP技术)关注的是对数据进行怎样的处理。

实例1:对象应用造一辆车

#include

#define FULL_GAS 85 

class Car//让我们来造辆车,定义类Car ,C++允许在类里面声明常量,但不允许对它进行赋值 
{
public:
std::string color;
std::string engine;
unsigned int gas_tank;//油缸 
unsigned int wheel;

void setColor(std::string col);
void setEngine(std::string eng);
void setWheel(unsigned int whe);
void filltank(int liter);//加油
int running(void);//动
void warning(void); 
};
void Car::setColor(std::string col) 
{
color = col;
}
void Car::setEngine(std::string eng)
{
engine = eng;
}
void Car::setWheel(unsigned int whe)
{
wheel = whe;

void Car::filltank(int liter)//函数(又称方法)的定义 
{
gas_tank += liter;
}
int Car::running(void)
{
std::cout << "我正在以120的时速往前移动。。。\n";
gas_tank--;
std::cout << "当前还剩 " << 100*gas_tank/FULL_GAS << "%" << "油量!\n";

return gas_tank; 

void Car::warning(void)
{
std::cout << "WARNING!!" << "\n还剩 " <<  100*gas_tank/FULL_GAS << "%" << "油量!\n";

int main()
{
char i;
Car mycar, car1;

mycar.setColor("WHITE");
mycar.setEngine("V8");
mycar.setWheel(4);

mycar.gas_tank = FULL_GAS;

while(mycar.running() )
{
if(mycar.gas_tank < 10)
{
mycar.warning();
std::cout << "请问是否需要加满油再行驶?(Y/N)\n";
std::cin >> i;
if( 'Y'== i || 'y' == i)
{
mycar.filltank(FULL_GAS); 
}
}

}   
return 0;
}

第十五讲:构造器和析构器

构造器和通常方法的主要区别:

1、构造器的名字必须和它所在类的名字一样  

2、系统在创建某个类的对象时会第一时间自动调用这个类的构造器  

3、构造器永远不会返回任何值

实例1:构造器的运用

#include

#define FULL_GAS 85 

class Car//让我们来造辆车,定义类Car ,C++允许在类里面声明常量,但不允许对它进行赋值 
{
public:
std::string color;
std::string engine;
unsigned int gas_tank;//油缸 
unsigned int wheel;

Car(void);//类Car的构造函数,系统会自动调用
void setColor(std::string col);
void setEngine(std::string eng);
void setWheel(unsigned int whe);
void filltank(int liter);//加油
int running(void);//动
void warning(void); 
};
Car::Car(void)
{
color = "White";
engine = "V8";
wheel = 4;
gas_tank = FULL_GAS;

void Car::setColor(std::string col) 
{
color = col;
}
void Car::setEngine(std::string eng)
{
engine = eng;
}
void Car::setWheel(unsigned int whe)
{
wheel = whe;

void Car::filltank(int liter)//函数(又称方法)的定义 
{
gas_tank += liter;
}
int Car::running(void)
{
char i;

std::cout << "我正在以120的时速往前移动。。。\n";
gas_tank--;
std::cout << "当前还剩 " << 100*gas_tank/FULL_GAS << "%" << "油量!\n";

if(gas_tank < 10)
{
warning();
std::cout << "请问是否需要加满油再行驶?(Y/N)\n";
std::cin >> i;
if( 'Y'== i || 'y' == i)
{
filltank(FULL_GAS); 
}

if(gas_tank == 0)
{
std::cout << "抛瞄中。。。。。";
return 0;
}
}

return gas_tank; 

void Car::warning(void)
{
std::cout << "WARNING!!" << "\n还剩 " <<  100*gas_tank/FULL_GAS << "%" << "油量!\n";

int main()
{
Car mycar;

mycar.gas_tank = FULL_GAS;

while(mycar.running())//有油则继续跑,没有油则结束
{
;
}   
return 0;
}

在销毁一个对象时,系统会调用另一个特殊方法,即析构器。

一般来说,构造器用来完成事先的初始化和准备互作(申请分配内存);析构器用来完成事后所必须的清理工作(清理内存)

析构器不返回任何值,也不带参数

实例2:构造器与析构器的共同应用

#include
#include
#include

class StoreQuote//定义类StoreQuote 
{
public:
std::string quote,speaker;
std::ofstream fileOutput;//定义文件对象fileOutput 

StoreQuote();//构造器 
~StoreQuote();//析构器

void inputQuote();//名言输入 
void inputSpeaker();//作者输入 
bool write();//写文件 
};

StoreQuote::StoreQuote()
{
fileOutput.open("test.txt", std::ios::app);//以附加(app)的形式打开文件 
}

StoreQuote::~StoreQuote()//关闭文件,释放内存 
{
fileOutput.close();
}

void StoreQuote::inputQuote()
{
std::getline(std::cin, quote);
}
void StoreQuote::inputSpeaker()
{
std::getline(std::cin, speaker);
}

bool StoreQuote::write()
{
if(fileOutput.is_open())//文件是否打开成功 
{
fileOutput << quote << "|" << speaker << "\n";//写入数据 
return true;
}
else
{
return false;
}
}

int main()
{
StoreQuote quote;//使用StoreQuote类定义一个对象quote 

std::cout << "请输入一句名言:\n";
quote.inputQuote();

std::cout << "请输入作者:\n";
quote.inputSpeaker();

if(quote.write())//文件是否写入成功 
{
std::cout << "成功写入文件"; 
}
else
{
std::cout << "写入文件失败"; 
return 1;
}
return 0;
}

第十六讲:this指针和类的继承

this指针指的是指向当前类生成的对象

继承机制使得程序员可以创建一个类的堆叠层次结构,每个子类均将继承在它的基类定义的方法和属性。

简单地说,就是通过继承机制,可以对现有的代码进行进一步扩展,并应用到新的程序中。

基类可以派生出其它的类,也称为父类或超类(如动物类)

子类是从基类派生出来的类(如乌龟类、猪类)

实例1:子类与基类的运用

#include
#include

class Animal//定义Animal类
{
public:
std::string mouth;
void eat();
void sleep();
void drool();//流鼻涕
};

class Pig:public Animal//类Pig继承于类Animal
{
public:
void climb();
};
class Turtle:public Animal//类Turtle继承于类Animal
{
public:
void swim();
};

void Animal::eat()//Animal类中的方法函数
{
std::cout << "I'm eatting!" << std::endl;
}
void Animal::sleep()
{
std::cout << "I'm sleeping!Don't disturb me!" << std::endl;
}
void Animal::drool()
{
std::cout << "我是公的,看到母的会流口水 流口水。。。" << std::endl;
}
void Pig::climb()//类Pig中的climb方法函数
{
std::cout << "我是一只漂亮的小母猪猪,我会上树,邱。。。" << std::endl;
}
void Turtle::swim()//类Turtle中的swim方法函数
{
std::cout << "我是一只小甲鱼,当母猪抓我,我就游到海里,哈。。。" << std::endl;
}
int main()
{
Pig pig;
Turtle turtle;//定义一个对象turtle

    pig.eat();
turtle.eat();
pig.climb();
turtle.swim();

    return 0; }第十七讲:继承机制中的构造器和析构器

实例1:构造器及其参数的继承应用

#include
#include

class Animal//定义Animal类
{
public:
std::string mouth;
std::string name;

Animal(std::string theName);//类Animal的构造器 
void eat();
void sleep();
void drool();//流鼻涕
};
Animal::Animal(std::string theName)//类Animal构造器函数 
{
name = theName;
}

class Pig:public Animal//类Pig继承于类Animal
{
public:
Pig(std::string theName);//类Pig的构造器
void climb();
};
Pig::Pig(std::string theName):Animal(theName)//类Pig的构造函数继承于类Animal的构造函数 
{

class Turtle:public Animal//类Turtle继承于类Animal
{
public:
Turtle(std::string theName);//类Pig的构造器
void swim();
};
Turtle::Turtle(std::string theName):Animal(theName)//类Turtle的构造函数继承于类Animal的构造函数参数 
{

}

void Animal::eat()//Animal类中的方法函数
{
std::cout << "I'm eatting!" << std::endl;
}
void Animal::sleep()
{
std::cout << "I'm sleeping!Don't disturb me!" << std::endl;
}
void Animal::drool()
{
std::cout << "我是公的,看到母的会流口水 流口水。。。" << std::endl;
}

void Pig::climb()//类Pig中的climb方法函数
{
std::cout << "我是一只漂亮的小母猪猪,我会上树,邱。。。" << std::endl;
}
void Turtle::swim()//类Turtle中的swim方法函数
{
std::cout << "我是一只小甲鱼,当母猪抓我,我就游到海里,哈。。。" << std::endl;
}
int main()
{
Pig pig("小猪猪");//定义一个对象pig,由于其继承于类Animal,
//且构造函数也继承于类Animal的构造函数,所以参数"小猪猪"将赋值给类pig中的名字属性name 
Turtle turtle("小甲鱼");//定义一个对象turtle

std::cout << "这只猪的名字是:" << pig.name << std::endl;
std::cout << "每只乌龟都有个伟大的名字:" << turtle.name << std::endl; 

    pig.eat();
turtle.eat();
pig.climb();
turtle.swim();

    return 0;
}

实例2:构造器与析构器的综合运用

#include
#include
//由于继承,编译后将首先调用基类构造器,然后调用子类构造器,然后运行其它程序;
//运行到程序末尾时,先调用子类析构器,让后调用基类析构器。
class BaseClass//定义基类BaseClass 
{
public:
BaseClass();//构造器
~BaseClass();//析构器

void doSomething();    
};  

class SubClass : public BaseClass定义子类SubClass,类SubClass继承于类BaseClass 
{
public:
SubClass();
~SubClass();
};

BaseClass::BaseClass()//类BaseClass构造器函数 
{
std::cout << "进入基类构造器。。。\n";
std::cout << "我在基类构造器里边干了某些事。。。\n\n";
}

BaseClass::~BaseClass()//类BaseClass析构器函数 
{
std::cout << "进入基类析构器。。。\n";
std::cout << "我在基类析构器里边干了某些事。。。\n\n";
}
void BaseClass::doSomething()//基类的doSomething函数 
{
std::cout << "我干了某些事。。。\n\n";

SubClass::SubClass()//子类SubClass的构造函数 
{
std::cout << "进入子类构造器。。。\n";
std::cout << "我在子类构造器里面还干了某些事。。。\n\n";
}
SubClass::~SubClass()//子类SubClass的析构函数 
{
std::cout << "进入子类析构器。。。\n";
std::cout << "我在子类析构器里面还干了某些事。。。\n\n";
}
int main()
{
SubClass subclass;
subclass.doSomething();

std::cout << "完事,收工!\n"; 
return 0;
}

第十八讲:访问控制

访问控制:C++提供用来保护(保护意思是对谁可以调用某个方法和访问某一个属性加上一个限制)类里的方法和属性的手段

实例1:访问级别应用

//级别            允许谁来访问
//public           任何代码 
//protected        这个类本身和它的子类 
//private          只有这个类本身
#include
#include

class Animal//定义Animal类
{
public:
std::string mouth;
//std::string name;

Animal(std::string theName);//类Animal的构造器 
void eat();
void sleep();
void drool();//流鼻涕
protected:
std::string name;
};
Animal::Animal(std::string theName)//类Animal构造器函数 
{
name = theName;
}

class Pig:public Animal//类Pig继承于类Animal
{
public:
Pig(std::string theName);//类Pig的构造器
void climb();
};
Pig::Pig(std::string theName):Animal(theName)//类Pig的构造函数继承于类Animal的构造函数 
{

class Turtle:public Animal//类Turtle继承于类Animal
{
public:
Turtle(std::string theName);//类Pig的构造器
void swim();
};
Turtle::Turtle(std::string theName):Animal(theName)//类Turtle的构造函数继承于类Animal的构造函数参数 
{

}

void Animal::eat()//Animal类中的方法函数
{
std::cout << "I'm eatting!" << std::endl;
}
void Animal::sleep()
{
std::cout << "I'm sleeping!Don't disturb me!" << std::endl;
}
void Animal::drool()
{
std::cout << "我是公的,看到母的会流口水 流口水。。。" << std::endl;
}

void Pig::climb()//类Pig中的climb方法函数
{
std::cout << "我是一只漂亮的小母猪猪,我会上树,邱。。。" << std::endl;
}
void Turtle::swim()//类Turtle中的swim方法函数
{
std::cout << "我是一只小甲鱼,当母猪抓我,我就游到海里,哈。。。" << std::endl;
}
int main()
{
Pig pig("小猪猪");//定义一个对象pig,由于其继承于类Animal,
//且构造函数也继承于类Animal的构造函数,所以参数"小猪猪"将赋值给类pig中的名字属性name 
Turtle turtle("小甲鱼");//定义一个对象turtle

//pig.name = "小甲鱼"; //由于name被保护起来,所以赋值将报错 

std::cout << "这只猪的名字是:" << pig.name << std::endl;
std::cout << "每只乌龟都有个伟大的名字:" << turtle.name << std::endl; 

    pig.eat();
turtle.eat();
pig.climb();
turtle.swim();

    return 0;
}

使用private的好处:可以只修改某个类的内部实现,而不必重新修改整个程序。因为其他代码根本就访问不到private保护的内容

在同一个类中可以使用多个public:,private和protected:语句,但最好把你的元素集中到一个地方,可读性会更好。

protected

把基类的访问级别改为protected,如果原来是public的话。这将使得这个子类外部的代码无法通过子类去访问基类中的public

private

是在告诉编译器从基类继承来的每一个成员都当做private来对待,这意味着只有这个子类可以使用它从基类继承来的元素。

第十九讲:覆盖方法和重载方法

覆盖是指派生类函数覆盖基类函数(函数名字参数相同且基类函数必须有virtual关键字)

以上学习可知可以通过创建新的子类来重用现有的代码(继承)

当我们需要在基类里提供一个通用的函数,但在它的某个子类里需要修改这个方法的实现,覆盖(overriding)就可以做到。

实例1:覆盖应用

//级别            允许谁来访问
//public           任何代码 
//protected        这个类本身和它的子类 
//private          只有这个类本身
#include
#include

class Animal//定义Animal类
{
public:
std::string mouth;
//std::string name;

Animal(std::string theName);//类Animal的构造器 
void eat();
void sleep();
void drool();//流鼻涕
protected:
std::string name;
};
Animal::Animal(std::string theName)//类Animal构造器函数 
{
name = theName;
}

class Pig:public Animal//类Pig继承于类Animal
{
public:
Pig(std::string theName);//类Pig的构造器
void climb();
void eat();   //new  再一次声明,以便原函数可以覆盖修改 
};
Pig::Pig(std::string theName):Animal(theName)//类Pig的构造函数继承于类Animal的构造函数 
{

class Turtle:public Animal//类Turtle继承于类Animal
{
public:
Turtle(std::string theName);//类Pig的构造器
void swim();
void eat();   //new  再一次声明,以便原函数可以覆盖修改 
};
Turtle::Turtle(std::string theName):Animal(theName)//类Turtle的构造函数继承于类Animal的构造函数参数 
{

}

void Animal::eat()//Animal类中的方法函数
{
std::cout << "I'm eatting!" << std::endl;
}
void Animal::sleep()
{
std::cout << "I'm sleeping!Don't disturb me!" << std::endl;
}
void Animal::drool()
{
std::cout << "我是公的,看到母的会流口水 流口水。。。" << std::endl;
}

void Pig::climb()//类Pig中的climb方法函数
{
std::cout << "我是一只漂亮的小母猪猪,我会上树,邱。。。" << std::endl;
}
void Turtle::swim()//类Turtle中的swim方法函数
{
std::cout << "我是一只小甲鱼,当母猪抓我,我就游到海里,哈。。。" << std::endl;
}
void Pig::eat()//重新声明eat方法 
{
Animal::eat();//可省去 
std::cout << "我在吃鱼\n\n" << std::endl;
}
void Turtle::eat()//重新声明eat方法 
{
Animal::eat();//可省去
std::cout << "我正在吃东坡肉!\n\n" << std::endl;
}

int main()
{
Pig pig("小猪猪");//定义一个对象pig,由于其继承于类Animal,
//且构造函数也继承于类Animal的构造函数,所以参数"小猪猪"将赋值给类pig中的名字属性name 
Turtle turtle("小甲鱼");//定义一个对象turtle

//pig.name = "小甲鱼"; //由于name被保护起来,所以赋值将报错 

//  std::cout << "这只猪的名字是:" << pig.name << std::endl;        
//    std::cout << "每只乌龟都有个伟大的名字:" << turtle.name << std::endl; 

    pig.eat();
turtle.eat();
pig.climb();
turtle.swim();

    return 0;
}

重载机制使你可以定义多个同名的方法(函数),只是它们的输入参数必须不同。(因为编译器是依靠不同的输入参数来区分不同的方法)

实例2:重载应用

//级别            允许谁来访问
//public           任何代码 
//protected        这个类本身和它的子类 
//private          只有这个类本身
#include
#include

class Animal//定义Animal类
{
public:
std::string mouth;
//std::string name;

Animal(std::string theName);//类Animal的构造器 
void eat();
void eat(int eatCount); 
void sleep();
void drool();//流鼻涕
protected:
std::string name;
};
Animal::Animal(std::string theName)//类Animal构造器函数 
{
name = theName;
}

class Pig:public Animal//类Pig继承于类Animal
{
public:
Pig(std::string theName);//类Pig的构造器
void climb();
};
Pig::Pig(std::string theName):Animal(theName)//类Pig的构造函数继承于类Animal的构造函数 
{

class Turtle:public Animal//类Turtle继承于类Animal
{
public:
Turtle(std::string theName);//类Pig的构造器
void swim();
};
Turtle::Turtle(std::string theName):Animal(theName)//类Turtle的构造函数继承于类Animal的构造函数参数 
{

}

void Animal::eat()//Animal类中的方法函数
{
std::cout << "I'm eatting!" << std::endl;
}
void Animal::eat(int eatCount)
{
std::cout << "我吃了" << eatCount << "碗馄饨!\n";
}
void Animal::sleep()
{
std::cout << "I'm sleeping!Don't disturb me!" << std::endl;
}
void Animal::drool()
{
std::cout << "我是公的,看到母的会流口水 流口水。。。" << std::endl;
}

void Pig::climb()//类Pig中的climb方法函数
{
std::cout << "我是一只漂亮的小母猪猪,我会上树,邱。。。" << std::endl;
}
void Turtle::swim()//类Turtle中的swim方法函数
{
std::cout << "我是一只小甲鱼,当母猪抓我,我就游到海里,哈。。。" << std::endl;
}

int main()
{
Pig pig("小猪猪");//定义一个对象pig,由于其继承于类Animal,
//且构造函数也继承于类Animal的构造函数,所以参数"小猪猪"将赋值给类pig中的名字属性name 
Turtle turtle("小甲鱼");//定义一个对象turtle

//pig.name = "小甲鱼"; //由于name被保护起来,所以赋值将报错 
//  std::cout << "这只猪的名字是:" << pig.name << std::endl;        
//    std::cout << "每只乌龟都有个伟大的名字:" << turtle.name << std::endl; 

    pig.eat();
turtle.eat();

pig.eat(15);
pig.climb();
turtle.swim();

    return 0;
}
在对方法进行覆盖(注意区分覆盖和重载)时,一定要看仔细,因为只要声明的输入参数和返回值与原来的不一致,你编写出来的就将是一个重载的方法而不是覆盖方法。

实例3:子类内声明重载

//级别            允许谁来访问
//public           任何代码 
//protected        这个类本身和它的子类 
//private          只有这个类本身
#include
#include

class Animal//定义Animal类,基类 
{
public:
std::string mouth;
//std::string name;

Animal(std::string theName);//类Animal的构造器 
void eat();

void sleep();
void drool();//流鼻涕
protected:
std::string name;
};
Animal::Animal(std::string theName)//类Animal构造器函数 
{
name = theName;
}

class Pig:public Animal//类Pig继承于类Animal,子类 
{
public:
Pig(std::string theName);//类Pig的构造器
void climb();
void eat(int eatCount);//重载 
};
Pig::Pig(std::string theName):Animal(theName)//类Pig的构造函数继承于类Animal的构造函数 
{

class Turtle:public Animal//类Turtle继承于类Animal
{
public:
Turtle(std::string theName);//类Pig的构造器
void swim();
};
Turtle::Turtle(std::string theName):Animal(theName)//类Turtle的构造函数继承于类Animal的构造函数参数 
{

}

void Animal::eat()//Animal类中的方法函数
{
std::cout << "I'm eatting!" << std::endl;
}

void Animal::sleep()
{
std::cout << "I'm sleeping!Don't disturb me!" << std::endl;
}
void Animal::drool()
{
std::cout << "我是公的,看到母的会流口水 流口水。。。" << std::endl;
}

void Pig::climb()//类Pig中的climb方法函数
{
std::cout << "我是一只漂亮的小母猪猪,我会上树,邱。。。" << std::endl;
}
void Pig::eat(int eatCount)
{
std::cout << "我吃了" << eatCount << "碗馄饨!\n";
}

void Turtle::swim()//类Turtle中的swim方法函数
{
std::cout << "我是一只小甲鱼,当母猪抓我,我就游到海里,哈。。。" << std::endl;
}

int main()
{
Pig pig("小猪猪");//定义一个对象pig,由于其继承于类Animal,
//且构造函数也继承于类Animal的构造函数,所以参数"小猪猪"将赋值给类pig中的名字属性name 
Turtle turtle("小甲鱼");//定义一个对象turtle

//pig.name = "小甲鱼"; //由于name被保护起来,所以赋值将报错 
//  std::cout << "这只猪的名字是:" << pig.name << std::endl;        
//    std::cout << "每只乌龟都有个伟大的名字:" << turtle.name << std::endl; 

    //pig.eat();   由于eat在子类中被重载,所以不能再被调用;如果在基类重载则可以调用 
turtle.eat();

pig.eat(15);//重载函数 
pig.climb();
turtle.swim();

    return 0;
}

第二十讲:一种特殊的友情关系——友元关系

友元关系是类之间的一种特殊关系,这种关系不仅允许友元类访问对方的public方法和属性,还允许友元访问对方的protected和private方法和属性。

实例1:友元关系访问保护量

#include
#include

using namespace std;
class Lovers//爱人关系,基类 
{
public:
Lovers(string theName);//
void kiss(Lovers *lover);
void ask(Lovers *lover, string something);

protected:
string name;

friend class Others; //祸根,交友不慎。。。。 (友元关系),则Others可以访问name 
};

class Boyfriend:public Lovers//Boyfriend子类,继承Lovers基类 
{
public:
Boyfriend(string theName);
};
class Girlfriend:public Lovers//girlfriend子类,继承Lovers基类 
{
public:
Girlfriend(string theName);
};

class Others//Others类 ,并没继承Lovers基类 
{
public:
Others(string theName);
void kiss(Lovers *lover);

protected:
string name;
};

Lovers::Lovers(string theName)//Lovers类的构造函数 
{
name = theName;
}
void Lovers::kiss(Lovers *lover)//Lovers *lover表示定义Lovers类的一个对象*lover 
{
cout << name << "亲亲我们家的" << lover->name << endl;

void Lovers::ask(Lovers *lover,string something)
{
cout << "宝贝儿" << lover->name << "帮我" << something << endl;
}
Boyfriend::Boyfriend(string theName):Lovers(theName)
{
}
Girlfriend::Girlfriend(string theName):Lovers(theName)
{
}
Others::Others(string theName)
{
name = theName;
}
void Others::kiss(Lovers *lover)
{
cout << name << "亲一下" << lover->name << endl;
}
int main()
{
Boyfriend boyfriend("A君");
Girlfriend girlfriend("B妞");

Others others("路人甲");

girlfriend.kiss(&boyfriend);
girlfriend.ask(&boyfriend,"洗衣服啦");

cout << "\n当当当当!传说中的路人甲登场啦。。。。\n";
others.kiss(&girlfriend);

return 0; 
}

第二十一讲:静态属性和静态方法

面对对象编程技术的一个重要特征是用一个对象把数据和对数据处理的方法封装在一起。

在前面的例子里,我们一直是在使用对象(也可以说某个类的实例)来调用方法,每个方法只处理调用它的那个对象所包含的数据,所有的数据都属于同一个对象。

C++允许我们把一个或多个成员声明为属于某个类,而不是仅属于该类的对象。(就是说这个类仅能让该类强暴)。好处是,程序员可以在没有创建任何对象的情况下调用有关的方法。另一个好处是能够让有关的数据仍在该类的所有对象间共享。

实例1:静态变量实例

#include
#include

using namespace std;
class Pet//定义Pet类 
{
public:
Pet(string theName);//构造器,对count进行写操作 
~Pet();//析构器,对count进行写操作  

static int getCount();//定义静态函数,对count进行读操作 

protected://不允许没有继承Pet类的其它类调用 
string name;
private://定义的变量和函数只服务于Pet类中的函数
static int count;//定义静态变量(属性) 
};
int Pet::count = 0;//注意这一句,他起码做了两件事(为count分配静态内存,初始化为0) 
Pet::Pet(string theName)//构造函数 
{
name = theName;
count++;

cout << "一只宠物出生了,名字叫做:" << name << "\n"; 

Pet::~Pet()//析构函数 
{
count--;
cout << "\n" << name << "挂掉了\n";

int Pet::getCount()//返回count值 
{
return count;
}

class Dog:public Pet//定义Dog类继承于Pet类 
{
public:
Dog(string theName);
};
Dog::Dog(string theName):Pet(theName)//Dog的构造函数继承于Pet类中的Pet(theName)函数,即该构造函数调用时将调用Pet函数 
{
}

class Cat:public Pet//定义Cat类继承于Pet类 
{
public:
Cat(string theName);
};
Cat::Cat(string theName):Pet(theName)//Cat的构造函数继承于Pet类中的Pet(theName)函数
{
}

int main()
{
Dog dog("Tom");
Cat cat("Jerry");

cout << "\n已经诞生了" << Pet::getCount()  << "只宠物!\n\n";
//如果有括号的话,编译器是在括号结束时调用dog_2与cat_2的析构函数,而没有的话,则是程序结尾处调用析构函数 
//    {
Dog dog_2("Tom_2");
Cat cat_2("Jerry_2");

cout << "\n现在呢,已经诞生了" << Pet::getCount() << "只宠物!";
//    }
cout << "\n现在还剩下" << Pet::getCount() << "只宠物!";

return 0;
}

第二十二讲:静态属性和静态方法2

规则:

静态成员是所有对象共享的,所以不能在静态方法里访问非静态的元素。(因为每个对象都有自己的this指针,静态方法不是属于某个特定的对象,而是由全体对象共享的,静态成员存储地址与对象并不在一起)

非静态方法可以访问类的静态成员,也可以访问类的非静态成员。

this指针是类的一个自动生成、自动隐藏的私有成员,它存在于类的非静态成员函数中,指向被调用函数所在的对象的地址。

当一个对象被创建时,该对象的this指针就自动指向对象数据的首地址。

当我们调用一个方法时,this指针都会随着你提供的输入参数被秘密的传递给那个方法。

实例1:this指针应用

#include
#include

using namespace std;
class Pet//定义Pet类 
{
public:
Pet(string theName);//构造器,对count进行写操作 
~Pet();//析构器,对count进行写操作  

static int getCount();//定义静态函数,对count进行读操作 

protected://不允许没有继承Pet类的其它类调用 
string name;
private://定义的变量和函数只服务于Pet类中的函数
static int count;//定义静态变量(属性) 
};
int Pet::count = 0;//注意这一句,他起码做了两件事(为count分配静态内存,初始化为0) 外部对静态属性进行声明 
Pet::Pet(string theName)//构造函数 
{
name = theName;
count++;

cout << "一只宠物出生了,名字叫做:" << name << "\n"; 

Pet::~Pet()//析构函数 
{
count--;
cout << "\n" << name << "挂掉了\n";

int Pet::getCount()//返回count值 
{
return count;
}

class Dog:public Pet//定义Dog类继承于Pet类 
{
public:
Dog(string theName);
};
Dog::Dog(string theName):Pet(theName)//Dog的构造函数继承于Pet类中的Pet(theName)函数,即该构造函数调用时将调用Pet函数 
{
cout << "this:" << this << "\n";//打印this指针地址 
}

class Cat:public Pet//定义Cat类继承于Pet类 
{
public:
Cat(string theName);
};
Cat::Cat(string theName):Pet(theName)//Cat的构造函数继承于Pet类中的Pet(theName)函数
{
}

int main()
{
Dog dog("Tom");
Cat cat("Jerry");

cout << "dog:" << &dog << "\n";//打印指向dog对象的this指针地址 
cout << "\n已经诞生了" << Pet::getCount()  << "只宠物!\n\n";
//如果有括号的话,编译器是在括号结束时调用dog_2与cat_2的析构函数,而没有的话,则是程序结尾处调用析构函数 
//    {
Dog dog_2("Tom_2");
Cat cat_2("Jerry_2");

cout << "\n现在呢,已经诞生了" << Pet::getCount() << "只宠物!";
//    }
cout << "\n现在还剩下" << Pet::getCount() << "只宠物!";

return 0;
}

第二十三讲:虚方法

TIPS:

引发问题的源头是我们使用了new在程序运行的时候才为dog和cat分配Dog类型和Cat类型的指针。这些都是它们在运行时才分配的类型,和它们在编译时的类型是不一样的。

如果拿不准要不要把某种方法声明为虚方法,那么就把它声明为虚方法好了。

在基类里把所有的方法都声明为虚方法会让最终的可执行代码的速度变得稍微慢一些,但好处是可以一劳永逸地确保程序的行为符合你的预期。

实例:虚方法的运用

//int *pointer = new int;//声明一个整型指针,并在程序运行时将它指向分配的内存空间 
//delect *pointer;//删除指针,释放内存 
#include
#include

using namespace std;

class Pet//定义Pet类 
{
public:
Pet(string theName);//构造器,对count进行写操作 

void eat();
void sleep();
virtual void play();//定义虚方法play,将在编译时为其分配内存 

protected://不允许没有继承Pet类的其它类调用 
string name;
};
Pet::Pet(string theName)
{
name = theName;
}
void Pet::eat()
{
cout << name << "正在吃东西!\n";
}
void Pet::sleep()
{
cout << name << "正在睡大觉!\n";
}
void Pet::play()
{
cout << name << "正在玩儿!\n";
}

class Cat:public Pet//定义Cat类继承于Pet类 
{
public:
Cat(string theName);//构造器

void climb();
void play();
};
Cat::Cat(string theName):Pet(theName)//Cat的构造函数继承于Pet类中的Pet(theName)函数
{
}
void Cat::climb()
{
cout << name << "正在爬树!\n";
}
void Cat::play()
{
cout << name << "玩毛绒球!\n";
}

class Dog:public Pet//定义Dog类继承于Pet类 
{
public:
Dog(string theName);//构造器

void bark();
void play();
};
Dog::Dog(string theName):Pet(theName)//Dog的构造函数继承于Pet类中的Pet(theName)函数
{
}
void Dog::bark()
{
cout << name << "汪汪汪!\n";
}
void Dog::play()
{
Pet::play();
cout << name << "正在追赶那只该死的猫!\n";
}
int main()
{
Pet *cat = new Cat("加菲");
Pet *dog = new Dog("欧迪");

cat -> sleep();
cat -> eat();
cat -> play();

dog -> sleep();
dog -> eat();
dog -> play();

delete cat;
delete dog;

return 0;
}  

第二十四讲:抽象方法抽象方法(abstract method,也可以成为纯虚函数)

实例1:抽象方法应用

//int *pointer = new int;//声明一个整型指针,并在程序运行时将它指向分配的内存空间 
//delect *pointer;//删除指针,释放内存 
#include
#include

using namespace std;

class Pet//定义Pet类 
{
public:
Pet(string theName);//构造器,对count进行写操作 

virtual void eat();
virtual void sleep();
virtual void play() = 0;//定义抽象方法play,具体只需在继承类中声明实现即可,基类中不需要具体实现 

protected://不允许没有继承Pet类的其它类调用 
string name;
};
Pet::Pet(string theName)
{
name = theName;
}
void Pet::eat()
{
cout << name << "正在吃东西!\n";
}
void Pet::sleep()
{
cout << name << "正在睡大觉!\n";
}

class Cat:public Pet//定义Cat类继承于Pet类 
{
public:
Cat(string theName);//构造器

void climb();
void play();
};
Cat::Cat(string theName):Pet(theName)//Cat的构造函数继承于Pet类中的Pet(theName)函数
{
}
void Cat::climb()
{
cout << name << "正在爬树!\n";
}
void Cat::play()
{
cout << name << "玩毛绒球!\n";
}

class Dog:public Pet//定义Dog类继承于Pet类 
{
public:
Dog(string theName);//构造器

void bark();
void play();
};
Dog::Dog(string theName):Pet(theName)//Dog的构造函数继承于Pet类中的Pet(theName)函数
{
}
void Dog::bark()
{
cout << name << "汪汪汪!\n";
}
void Dog::play()
{
//Pet::play();
cout << name << "正在追赶那只该死的猫!\n";
}
int main()
{
Pet *cat = new Cat("加菲");
Pet *dog = new Dog("欧迪");

cat -> sleep();
cat -> eat();
cat -> play();

dog -> sleep();
dog -> eat();
dog -> play();

delete cat;
delete dog;

return 0;
}  

多态性:是指用一个名字定义不同的函数,调用同一个名字的函数,却执行不同的操作,从而实现传说中的“一个接口,多种方法”。

一般类的析构函数都是释放内存资源,如果析构函数不被调用的话将会造成内存泄漏。

析构器定义为虚方法是为了当一个基类的指针删除一个派生类的对象时,派生类的析构函数可以被正确调用。

当类里面有虚函数时,编译器会给类添加一个虚函数表,里面存放着虚函数指针。为了节省资源,只有当一个类被用来作为基类时,我们才把析构函数写成虚函数。

实例2:析构函数解析

#include
//析构器都是虚方法 
using namespace std;

class ClxBase//定义基类ClxBase 
{
public:
ClxBase()//构造器 
{
cout << "ClxBase begin!\n";
};
virtual ~ClxBase()//析构器 ,如果其不为虚函数,则子类的析构函数将不被执行,将造成内存泄漏 
{
cout << "ClxBase end!\n"; 
};
virtual void doSomething()//定义抽象方法doSomething 具体只需在继承类中声明实现即可(便可覆盖基类中定义),基类中可以不具体实现 
{
cout << "Do something in class ClxBase!\n"; 
}
};

class ClxDerived:public ClxBase//定义子类ClxDerived继承于基类ClxBase 
{
public:
ClxDerived()//子类构造器 
{
cout << "ClxDerived begin!\n";
};
~ClxDerived()//子类析构器 
{
cout << "Output from the destructor of class ClxDerived!\n";
};
void doSomething()
{
cout << "Do something in class ClxDerived!\n";
};
};

int main()
{
ClxBase *pTest = new ClxDerived;//定义一个pTest指针指向继承于ClxBase的ClxDerived对象,并为此指针分配内存 
pTest -> doSomething();

delete pTest;

return 0;
}

第二十五讲:运算符的重载

所谓重载,就是重新赋予新的含义,函数重载是对一个已有的函数赋予新的含义。

运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该运算符。也就是说,运算符重载是通过定义函数实现的,运算符重载实质是是函数的重载。

实例1:复数加法

#include

using namespace std; 
class Complex//定义基类
{
public:
Complex();//构造器 
Complex(double r,double i);//重载函数,输入参数不同,功能不同 
Complex Complex_add(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;

Complex Complex::Complex_add(Complex &d)//Complex &d为定义基类对象d(被加数) complex_add为基类Complex方法 
{                                       //第二个Complex为继承基类的加数对象 
Complex c;//定义基类对象c           //第一个Complex为其类型 

c.real = real + d.real;
c.imag = imag + d.imag;

return c;
}
void Complex::print()
{
cout << "(" << real << ", " << imag << "i)\n";

int main()
{
Complex c1(3,4),c2(5,-10),c3;//定义三个对象c1、c2、c3及其重载函数参数值 
c3 = c1.Complex_add(c2);

cout << "c1 = ";
c1.print();
cout << "c2 = ";
c2.print();
cout << "c1 + c2 = ";
c3.print();

return 0;
}

实例2:复数加法2

#include

using namespace std;
class Complex//定义基类
{
public:
Complex();//构造器 
Complex(double r,double i);//重载函数,输入参数不同,功能不同 
friend Complex operator+(Complex &c,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;

//注意:这里作为友元函数,不属于Complex,记得别写:: 
Complex operator+(Complex &c,Complex &d)//Complex &d为定义基类对象d(被加数) complex_add为基类Complex方法 
{
return Complex(c.real + d.real,c.imag + d.imag);
}
void Complex::print()
{
cout << "(" << real << ", " << imag << "i)\n";

int main()
{
Complex c1(3,4),c2(5,-10),c3;//定义三个对象c1、c2、c3及其重载函数参数值 
c3 = operator+(c1,c2);

cout << "c1 = ";
c1.print();
cout << "c2 = ";
c2.print();
cout << "c1 + c2 = ";
c3.print();

return 0;
}

作业:分数加减乘除的操作符重载

#include
#include
#include <math.h> 

using namespace std;
class Rational//定义基类
{
public:
Rational(int num,int denom); //构造器 num = 分子,denom = 分母 

Rational operator+(Rational rhs);// rhs == right hand side(右手边参数) 
Rational operator-(Rational rhs);
Rational operator*(Rational rhs);
Rational operator/(Rational rhs);

void print();//打印 

private:
void normalize();//负责对分数的简化处理

int numerator; //分子
int denominator; //分母 
}; 

Rational::Rational(int num,int denom)//构造函数实现 
{
numerator = num;
denominator = denom;

normalize(); 

//normalize()对分数进行简化操作包括:
//1.只允许分子为负数,如果分母为负数则把负数挪到分子部分,如1/-2==-1/2 
//2.利用欧几里德算法(辗转求余原理)将分数进行简化:2/10 => 1/5 

void Rational::normalize()
{
//确保分母为正
if( denominator < 0) 
{
numerator = -numerator;
denominator = -denominator;
}
//欧几里德算法 
int a = abs(numerator);
int b = abs(denominator);

//求出最大公约数
while(b>0)
{
int t = a % b;//取余 
a = b;
b = t;
}

//分子、分母分别除以最大公约数得到最简化分数
numerator /= a;
denominator /= a; 
}
//a   c   a*d   c*b   a*d + c*d
//- + - = --- + --- = ----------
//b   d   b*d   b*d      b*d
Rational Rational::operator+(Rational rhs)//分数的加运算 
{
int a = numerator;
int b = denominator;
int c = rhs.numerator;
int d = rhs.denominator;

int e = a*b + c*d;
int f = b*d;

return Rational(e,f);
}
//a   c   a   -c
//- - - = - + -- 
//b   d   b   d 
Rational Rational::operator-(Rational rhs)//分数的减运算 
{
rhs.numerator = -rhs.numerator; //被减数分子取负数 
return operator+(rhs);
}
//a   c   a*c
//- * - = --- 
//b   d   b*d 
Rational Rational::operator*(Rational rhs)//分数的乘运算
{
int a = numerator;
int b = denominator;
int c = rhs.numerator;
int d = rhs.denominator;

int e = a*c;
int f = b*d;

return Rational(e,f);
}
//a   c   a   d
//- / - = - * - 
//b   d   b   c
Rational Rational::operator/(Rational rhs)//分数的除运算
{
//rhs的分子分母互换 
int t = rhs.numerator;
rhs.numerator = rhs.denominator;
rhs.denominator = t;

return operator*(rhs);
}
void Rational::print() //打印最简分数 
{
if(numerator % denominator == 0)
cout << numerator / denominator;
else 
cout << numerator << "/" << denominator;
}

int main()
{
Rational f1(2,16);//定义f1对象,且传入(2,16)参数 
Rational f2(7,8);

//测试有理数加法运算 
Rational res = f1 + f2;//相当于res=f1.operator+(f2)
f1.print();
cout << " + ";
f2.print();
cout << " = ";
res.print();
cout << "\n"; 

//测试有理数减法运算 
res = f1 - f2;
f1.print();
cout << " - ";
f2.print();
cout << " = ";
res.print();
cout << "\n"; 

//测试有理数乘法运算 
res = f1 * f2;
f1.print();
cout << " * ";
f2.print();
cout << " = ";
res.print();
cout << "\n";

//测试有理数除法运算 
res = f1 / f2;
f1.print();
cout << " / ";
f2.print();
cout << " = ";
res.print();
cout << "\n";

return 0;
}

第二十六讲:运算符重载2

去重载一个操作符,应该只有在必要的时候,比如实现一种新的数据类型时,才重载操作符。

重载操作符的目的是为了让代码更容易阅读和理解。

第二十七讲:运算符左移<<重载

一般来说,在调用operator<<()重载函数时,传递给它的是哪一个流,它返回的就应该是那个流的一个引用。

实例:左移操作符重载

#include
#include
#include <math.h> 

using namespace std;
class Rational//定义基类
{
public:
Rational(int num,int denom); //构造器 num = 分子,denom = 分母 

Rational operator+(Rational rhs);// rhs == right hand side(右手边参数) 
Rational operator-(Rational rhs);
Rational operator*(Rational rhs);
Rational operator/(Rational rhs);

private:
void normalize();//负责对分数的简化处理

int numerator; //分子
int denominator; //分母 

friend ostream& operator<<(ostream& os,Rational f);//传递给它的是哪一个流,它返回的就是那个流的一个引用
}; 

Rational::Rational(int num,int denom)//构造函数实现 
{
numerator = num;
denominator = denom;

normalize(); 

//normalize()对分数进行简化操作包括:
//1.只允许分子为负数,如果分母为负数则把负数挪到分子部分,如1/-2==-1/2 
//2.利用欧几里德算法(辗转求余原理)将分数进行简化:2/10 => 1/5 

void Rational::normalize()
{
//确保分母为正
if( denominator < 0) 
{
numerator = -numerator;
denominator = -denominator;
}
//欧几里德算法 
int a = abs(numerator);
int b = abs(denominator);

//求出最大公约数
while(b>0)
{
int t = a % b;//取余 
a = b;
b = t;
}

//分子、分母分别除以最大公约数得到最简化分数
numerator /= a;
denominator /= a; 
}
//a   c   a*d   c*b   a*d + c*d
//- + - = --- + --- = ----------
//b   d   b*d   b*d      b*d
Rational Rational::operator+(Rational rhs)//分数的加运算 
{
int a = numerator;
int b = denominator;
int c = rhs.numerator;
int d = rhs.denominator;

int e = a*b + c*d;
int f = b*d;

return Rational(e,f);
}
//a   c   a   -c
//- - - = - + -- 
//b   d   b   d 
Rational Rational::operator-(Rational rhs)//分数的减运算 
{
rhs.numerator = -rhs.numerator; //被减数分子取负数 
return operator+(rhs);
}
//a   c   a*c
//- * - = --- 
//b   d   b*d 
Rational Rational::operator*(Rational rhs)//分数的乘运算
{
int a = numerator;
int b = denominator;
int c = rhs.numerator;
int d = rhs.denominator;

int e = a*c;
int f = b*d;

return Rational(e,f);
}
//a   c   a   d
//- / - = - * - 
//b   d   b   c
Rational Rational::operator/(Rational rhs)//分数的除运算
{
//rhs的分子分母互换 
int t = rhs.numerator;
rhs.numerator = rhs.denominator;
rhs.denominator = t;

return operator*(rhs);
}

ostream& operator<<(ostream& os,Rational f);//函数声明 

int main()
{
Rational f1(2,16);//定义f1对象,且传入(2,16)参数 
Rational f2(7,8);

//测试有理数加法运算 
cout << f1 << " + " << f2 << " == " << (f1+f2) << "\n"; // 左移操作符<<已经被重载了 

//测试有理数减法运算 
cout << f1 << " - " << f2 << " == " << (f1-f2) << "\n"; //<< f1中由于f1属于Rational类型,将自动转到打印分数形式输出 

//测试有理数乘法运算 
cout << f1 << " * " << f2 << " == " << (f1*f2) << "\n";//<< "+"中由于<<后面参数是字符串,所以使用系统默认的打印 

//测试有理数除法运算 
cout << f1 << " / " << f2 << " == " << (f1/f2) << "\n"; 

return 0;
}
ostream& operator<<(ostream& os,Rational f)//并不属于Rational类,是一个独立的函数 
{
if(f.numerator % f.denominator == 0)
os << f.numerator / f.denominator;
else 
os << f.numerator << "/" << f.denominator;//打印分数 
return os; 
}

第二十八讲:多继承(multiple inheritance)

多继承语法:

//助教类既继承于老师类,又继承于学生类 
class Assistant:public Teacher,public Student
{
};

当遇到的问题无法只用一个“是一个”关系来描述的时候,就是多继承出场的时候。例即是学生,又是人,还是助教。

#include
#include  

using namespace std;

#include
//人类 
class Person
{
public:
Person(string theName);//构造函数,theName为类的输入参数 
void introduce();
protected:
string name;
};
Person::Person(string theName)//构造函数实现 
{
name = theName;
}
void Person::introduce()//introduce()函数实现 
{
cout << "大家好,我是" << name << "。\n\n";
}

//老师类继承于人类 
class Teacher:public Person
{
public:
Teacher(string theName,string theClass);

void teach();//教书 
void introduce();
protected:
string classes;
};
Teacher::Teacher(string theName,string theClass):Person(theName)//老师的名字继承于人类中的名字 
{
classes = theClass;
}
void Teacher::teach()
{
cout<< name << " 教 "<< classes << "。\n\n";
}
void Teacher::introduce()
{
cout<<"大家好,我是 "<< name <<" ,我教 "<< classes << "。\n\n";
}

//学生类继承于人类
class Student:public Person 
{
public:
Student(string theName,string theClass);

void attendClass();//要上课 
void introduce();
protected:
string classes;
};
Student::Student(string theName,string theClass):Person(theName)//学生名字继承于人类中的名字 
{
classes = theClass;
}
void Student::attendClass()
{
cout<< name <<"加入"<< classes << "学习。\n\n";
}
void Student::introduce()
{
cout<< "大家好,我是" << name << ",我在" << classes << "学习。\n\n";
}

//助教类既继承于老师类,又继承于学生类 
class Assistant:public Teacher,public Student
{
public:
Assistant(string theName,string classTeaching,string classAttending);

void introduce();
};
Assistant::Assistant(string theName,string classTeaching,string classAttending):Teacher(theName, classTeaching),Student(theName,classAttending)
{

//多继承 助教既继承老师类,又继承学生类 

}
void Assistant::introduce()
{
cout << "大家好,我是" << Student::name << ".我教" << Teacher::classes << ",";
cout << "同时我在" << Student::classes << "学习。\n\n"; 
}

int main()
{
Teacher teacher("小甲鱼","C++入门班");
Student student("迷途羔羊","C++入门班");
Assistant assistant("丁丁","C++入门班","C++进阶班");

teacher.introduce();
teacher.teach();

student.introduce();
student.attendClass();

assistant.introduce();
assistant.teach();
assistant.attendClass();

return 0;
}

第二十九讲:虚继承

通过虚继承某个基类,就是在告诉编译器:从当前这个类再派生出来的子类只能拥有那个基类的一个实例。

虚继承的语法:

class Teacher:virtual public Person

{

}

这样做的话便可以让Student和Teacher类都虚继承自Person类,编译器将确保从Student和Teacher类再派生出来的子类只能拥有一份Person类的属性。

实例:虚继承应用

#include
#include  
//虚继承可以让Student和Teacher类都虚继承自Person类,
//编译器将确保从Student和Teacher类再派生出来的子类只能拥有一份Person类的属性。
using namespace std;

#include
//人类 
class Person
{
public:
Person(string theName);//构造函数,theName为类的输入参数 
void introduce();
protected:
string name;
};
Person::Person(string theName)//构造函数实现 
{
name = theName;
}
void Person::introduce()//introduce()函数实现 
{
cout << "大家好,我是" << name << "。\n\n";
}

//老师类继承于人类 
class Teacher:virtual public Person//虚继承,再有类继承Teacher类时,将只拥有一份Person类的属性。 
{
public:
Teacher(string theName,string theClass);

void teach();//教书 
void introduce();
protected:
string classes;
};
Teacher::Teacher(string theName,string theClass):Person(theName)//老师的名字继承于人类中的名字 
{
classes = theClass;
}
void Teacher::teach()
{
cout<< name << " 教 "<< classes << "。\n\n";
}
void Teacher::introduce()
{
cout<<"大家好,我是 "<< name <<" ,我教 "<< classes << "。\n\n";
}

//学生类继承于人类
class Student:virtual public Person//虚继承,再有类继承Student类时,将只拥有一份Person类的属性 
{
public:
Student(string theName,string theClass);

void attendClass();//要上课 
void introduce();
protected:
string classes;
};
Student::Student(string theName,string theClass):Person(theName)//学生名字继承于人类中的名字 
{
classes = theClass;
}
void Student::attendClass()
{
cout<< name <<"加入"<< classes << "学习。\n\n";
}
void Student::introduce()
{
cout<< "大家好,我是" << name << ",我在" << classes << "学习。\n\n";
}

//助教类既继承于老师类,又继承于学生类 
class Assistant:public Teacher,public Student
{
public:
Assistant(string theName,string classTeaching,string classAttending);

void introduce();
};
Assistant::Assistant(string theName,string classTeaching,string classAttending)
:Teacher(theName, classTeaching),Student(theName,classAttending),Person(theName)
//由于虚继承,Teacher和Student都不能拥有Person类中的属性和方法
//只能由Person类自己给出 
{
//多继承 助教既继承老师类,又继承学生类 
}
void Assistant::introduce()
{
cout << "大家好,我是" << Student::name << ".我教" << Teacher::classes << ",";
cout << "同时我在" << Student::classes << "学习。\n\n"; 
}

int main()
{
Teacher teacher("小甲鱼","C++入门班");
Student student("迷途羔羊","C++入门班");
Assistant assistant("丁丁","C++入门班","C++进阶班");

teacher.introduce();
teacher.teach();

student.introduce();
student.attendClass();

assistant.introduce();
assistant.teach();
assistant.attendClass();

return 0;
}

第三十讲:错误处理和调试

程序出错可以分为两大类:编译时出错(complie-time error)和运行时错误(run-time error)

suggest1:培养并保持一种编程风格

suggest2:认真对待编译器给出的错误/警告信息

suggest3:写代码三思而后行(先画流程图)

suggest4:注意检查最基本的语法

suggest5:把可能有问题的代码行注释

suggest6:换一个环境或开发工具试试

suggest7:检查自己是否已经把所有必要的头文件全部include进来

suggest8:留意变量的作用域和命名空间

suggest9:休息一下

suggest10:使用调试工具

最后避免错误的另一个好方法就是把调试好的代码另外保存起来并不再改动它,然后把代码划分成各个模块,用它们来搭建新的应用程序。

运行时错误:

1、培养保持良好编程风格    

2、多用注释,用好注释

3、注意操作符的优先级

4、不要忘记对用户输入和文件输入进行合理性检查

5、不要做任何假设,想当然

6、把程序划分成一些比较小的单元模块来测试

第三十一讲:错误处理与调试2

让函数返回错误代码

实例:范围限制

#include
#include

using namespace std;
class Factorial
{
public:
Factorial(unsigned short num);
unsigned long getFactorial();
bool inRange();

private:
unsigned short num;
};
Factorial::Factorial(unsigned short num)
{
this->num = num;//传入的参数num值给了Factorial类中的属性num 
}

unsigned long Factorial::getFactorial()
{
unsigned long sum = 1;
for(int i=1;i <= num; i++)
{
sum *= i;
}
return sum;
}
bool Factorial::inRange()
{
unsigned long max = ULONG_MAX;//ULONG_MAX在头文件climits中 
for(int i = num; i >= 1; --i)//将最大值ULONG_MAX除以输入的num阶乘 
{
max /= i;
}
if(max < 1)
return false;
else 
return true;
}

int main()
{
unsigned short num = 0;
cout << "请输入一个整数:";
cin >> num;

Factorial fac(num);

if(fac.inRange())
{
cout << num << "的阶乘值是" << fac.getFactorial() << "\n\n";

else
{
cout << "您所输入的值太大!\n\n";
}
}

第三十二讲:assert函数和捕获异常

C语言和C++都有一个专门为调试而准备的工具函数---assert函数。这个函数是在assert.h库文件里定义的。

实例1:assert函数应用

#include
//assert()函数需要有一个参数,它将测试这个输入参数的真或者假状态
//如果为真  Do nothing
//如果为假  Do something 
int main()
{
int i = 20;
assert( i == 65);//如果括号内为真,则过;假则中断程序 

return 0;

同样为了对付潜在的编程错误(尤其是在运行时的错误),捕获异常是一种完全不同的方法。简单说,异常就是与预期不相符合的反常现象。

注:每条try语句至少要有一条配对的catch语句。必须定义catch语句以便让它接收一个特定类型的参数。

当某个try语句块里执行过throw语句,它后面的所有语句(截止到这个try语句块末尾)将永远不会被执行。

如果try语句块无法找到一个与之匹配的catch语句块,它抛出的异常将中止程序的执行。

实例2:捕获异常(try,catch,throw)

#include
#include

using namespace std;
unsigned long returnFactorial(unsigned short num) throw(const char*);

int main()
{
unsigned short num = 0;
cout << "请输入一个整数:";
while( !(cin>>num) || (num < 1) )
{
cin.clear();//清楚状态
cin.ignore(100,'\n'); //清除缓存区
cout << "请输入一个整数:"; 
}
cin.ignore(100,'\n');
try
{
unsigned long factorial = returnFactorial(num);//如果异常,则抛出字符型的*e异常
//直接跳转到catch继续执行 
cout << num << "的阶乘值是:" << factorial;
}
catch(const char *e)//如果正常则不执行,抛出异常则执行 
{
cout << "error";
}
return 0;
}

unsigned long returnFactorial(unsigned short num) throw(const char*)
{
unsigned long sum = 1;
unsigned long max = ULONG_MAX;

for(int i = 1;i <= num; i++)
{
sum *= i;
max /= i;
}
if(max < 1)
{
throw"悲催。。。该基数太大,无法在该计算机计算求出阶乘值。\n";
}
else
{
return sum;
}
}

第三十三讲:动态的内存管理

到目前为止,所讲解的每个示例程序在完成它的任务时所使用的内存空间都是固定不变的。不能在程序运行期间动态增加或减少内存空间(即静态内存)。

在很多时候,需要存储的数据量到底有多大在事先往往是一个未知数,想要处理好这类情况,就需要在C++程序里使用动态内存。

动态内存由一些没有名字、只有地址的内存块构成,那些内存块是在程序运行期间动态分配的。他们来自一个由标准C++库替你管理的大池子(内存池),从内存池申请一些内存需要用到new语句,它将根据你提供的数据类型分配一块大小适当的内存。

new语句返回的内存块很可能充满“垃圾”数据,所以我们通常先往里边写一些东西覆盖,再访问它们,或者在类直接写一个构造器来初始化。

在使用动态内存时,最重要的原则是每一条new语句都必须有一条与之配对的delete语句,否则将导致内存泄漏。

delete语句只释放给定指针变量正指向的内存块,不影响这个指针。在执行delete语句之后,那个内存块被释放了,但指针变量还依然健在。

实例:动态内存管理

#include
#include

using namespace std;
class Company
{
public:
Company(string theName);
virtual void printInfo();//需要打印的一些信息

protected:
string name; 
};
Company::Company(string theName)//基类构造函数实现
{
name = theName; 

void Company::printInfo()
{
cout << "这家公司的名字叫:" << name << "。\n";
}

class TechCompany:public Company
{
public:
TechCompany(string theName,string product);
virtual void printInfo();//定义为虚方法,为了使科技公司类中的打印函数能够覆盖基类的打印函数

private:
string product; 
};
TechCompany::TechCompany(string theName,string product):Company(theName)//子类构造函数实现
{
this->product = product;
}
void TechCompany::printInfo()
{
cout << name << "公司大量生产了 " << product << "这款产品!\n";

int main()
{
Company *company = new Company("APPLE");//定义一个基类对象compnay,为其分配内存,指针指向其地址 
company -> printInfo();//调用基类的打印方法

delete company;//删除指针地址
company = NULL;//为指针指向的地址赋空值 

company = new TechCompany("APPLE","IPHONE");//指针指向新定义的子类TechCompany 
company -> printInfo();//调用子类的打印方法

delete company;
company = NULL; 

return 0;
}

第三十四讲:动态数组

虽然前面讲过的用new给基本类型和对象在运行时分配内存,但它们的尺寸在编译时就已经确定下来——因为我们为之申请内存的数据类型在程序中有明确的定义,有明确的单位长度。

但有些时候,必须等到程序运行时才能确定需要申请多少内存,甚至还需要根据程序的运行情况追加申请更多的内存。

例如:  int *x = new int[10];//x表示整型数组的数组名

实例:动态数组

#include
#include
//让new函数申请内存并返回一个指向内存块的指针 
using namespace std;
int main()
{
unsigned int count = 0;

cout << "请输入数组的元素个数:\n";
cin >> count;

int *x = new int[count];//在程序运行时才申请内存,内存块指针指向x 
for(int i=0;i<count;i++)
{
x[i] = i;

for(int i=0;i<count;i++)
{
cout << "x[" <<i << "]的值是:" << x[i] << "\n";


return 0;

第三十五讲:从函数或方法返回内存

在函数里调用new语句为某种对象或某种基本数据类型分配一块内存,再把那块内存的地址返回给程序的主代码,主代码将使用那块内存并在完成有关操作后立刻释放。

实例1:函数或方法返回内存

#include
using namespace std;

int *newInt(int value);//声明一个函数,由int *知其返回值为指向整型的一个地址 
int main()
{
int *x = newInt(20);//定义指针类型x 

cout << *x;
delete x;//释放地址 
x = NULL;//指针填充NULL 

return 0;


int *newInt(int value)
{
int *myInt = new int;//申请一个整型内存块(4个字节),并将指针(myInt)指向内存块的地址 
*myInt = value;//解引用,赋值 

return myInt;
}

函数指针:指向函数首地址的指针变量称为函数指针。

#include <stdio.h>

int fun(int x, int y);

int main()
{
int i,a,b;
int (*p)();  //声明函数指针

p = fun; //给函数指针p赋值,使它指向函数fun

printf("请输十个数字:\n");

for(i=0;i<10;i++)
{
scanf("%d",&b);
a = (*p)(a,b);  //通过指针p调用函数fun 


printf("The Max Number is:%d",a);

return 0;


fun(int x,int y)
{
int z;

z = (x>y)?x:y;

return(z);
}

指针函数:一个函数可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。

实例2:函数指针

#include

using namespace std; 

int *newInt(int value);//声明一个指针函数,其返回值为一个地址 

int main()
{
int *x = newInt(20);//新建一个整型变量空间,并将x指针指向这个空间 

cout << *x;
delete x;
x = NULL;

return 0;

int *newInt(int value)//指针函数的具体实现 
{
int *myInt = new int;
*myInt = value;

return myInt;
}

第三十六讲:副本构造器

我们可以把一个对象赋值给一个类型与之相同的变量,编译器将生成必要的代码把“源”对象各属性的值分别赋值给“目标”对象的对应成员。这种赋值行为称之为逐位复制。(但如果某些成员变量是指针的话,对象成员逐位复制的结果是你将拥有两个一模一样的实例,而这两个副本里的同名指针会指向相同的地址

实例1:带指针变量的等号重载

#include
#include

using namespace std;
class MyClass
{
public:
MyClass(int *p);//构造器
~MyClass();//析构器

MyClass &operator = (const MyClass &rhs);//运算符(=)重载 
void print();//打印 
private:
int *ptr; 
};

MyClass::MyClass(int *p)//构造器实现 
{
ptr = p;
}

MyClass::~MyClass()//析构器实现 
{
delete ptr;


// a = b; 如obj1 = obj2; 
MyClass &MyClass::operator=(const MyClass &rhs)//运算符(=)重载实现,rhs为另一对象 
{
if(this != &rhs)//obj1不等于obj2时,this指针指的是指向当前类生成的对象(此处指第一个MyClass) 
{
delete ptr;//删除地址ptr,释放obj1的内存 

ptr = new int;//创建新的内存给指针ptr 
*ptr = *rhs.ptr;//解引用,将obj2的值赋值给obj1 
//cout << "复制指针!\n";
}
else
{
cout << "赋值号两边为同个对象,不做处理!\n";//obj1 = obj2时 
}
return *this;//返回第一个MyClass对象 
}

void MyClass::print()//打印*ptr的值 
{
cout << *ptr << endl;
}

int main()
{
MyClass obj1(new int(1));//创建对象obj1 
MyClass obj2(new int(2));//创建对象obj2

obj1.print();//打印对象obj1中指针指向的地址的储存值 
obj2.print();//打印对象obj2中指针指向的地址的储存值 

obj2 = obj1;//obj1通过等号重载给obj2 

obj1.print();//打印对象obj1中指针指向的地址的储存值 
obj2.print();//打印对象obj2中指针指向的地址的储存值 

return 0;
}

实例2:副本构造器

#include
#include

using namespace std;
class MyClass
{
public:
MyClass(int *p);//主构造器
MyClass(const MyClass &rhs);//副本构造器 
~MyClass();//析构器

MyClass &operator = (const MyClass &rhs);//运算符(=)重载,对象复制,括号里声明对象调用副本构造器 
void print();//打印 
private:
int *ptr; 
};

MyClass::MyClass(int *p)//主构造器实现 
{
cout << "进入主构造器\n"; 
ptr = p;
cout << "离开主构造器\n";
}
MyClass::MyClass(const MyClass &rhs)//副本构造器实现 
{
cout << "进入副本构造器\n"; 
*this = rhs; //等号赋值重载 
cout << "离开副本构造器\n";
}

MyClass::~MyClass()//析构器实现 
{
cout << "进入析构器\n";
delete ptr;
cout << "离开析构器\n"; 


// a = b; 如obj1 = obj2; 
MyClass &MyClass::operator=(const MyClass &rhs)//运算符(=)重载实现,rhs为另一对象 
{
cout << "进入赋值语句重载\n";
if(this != &rhs)//obj1不等于obj2时,this指针指的是指向当前类生成的对象(此处指第一个MyClass) 
{
delete ptr;//删除地址ptr,释放obj1的内存 

ptr = new int;//创建新的内存给指针ptr 
*ptr = *rhs.ptr;//解引用,将obj2的值赋值给obj1 
//cout << "复制指针!\n";
}
else//obj1与obj2为同一个对象时 
{
cout << "赋值号两边为同个对象,不做处理!\n"; 
}
cout << "离开赋值语句重载\n";
return *this;//返回第一个MyClass对象 
}

void MyClass::print()//打印*ptr的值 
{
cout << *ptr << endl;
}

int main()
{
MyClass obj1(new int(1));//创建对象obj1 
MyClass obj2(new int(2));//创建对象obj2
obj2 = obj1;//obj1通过等号重载给obj2
obj1.print();//打印对象obj1中指针指向的地址的储存值 
obj2.print();//打印对象obj2中指针指向的地址的储存值 

cout << "---------------------------------------------\n"; 

MyClass obj3(new int(3));//创建对象obj3
MyClass obj4 = obj3;//副本构造器 
obj3.print();//打印对象obj3中指针指向的地址的储存值 
obj4.print();//打印对象obj4中指针指向的地址的储存值 

cout << "---------------------------------------------\n"; 

MyClass obj5(new int(5));//创建对象obj5
obj5 = obj5;
obj5.print();//打印对象obj5中指针指向的地址的储存值 

return 0;
}

第三十七讲:高级强制类型转换

实例1:静态强制转换

#include
#include

using namespace std;
class Company
{
public:
Company(string theName,string product);
virtual void printInfo();

protected:
string name;
string product;
};
Company::Company(string theName,string product)
{
name = theName;
this->product = product; 

void Company::printInfo()
{
cout << "这个公司的名字叫:" << name << "正在生产" << product << "\n";
}

class TechCompany:public Company
{
public:
TechCompany(string theName,string product);
virtual void printInfo();
};
TechCompany::TechCompany(string theName,string product):Company(theName,product)
{
}
void TechCompany::printInfo()
{
cout << name << "公司大量生产了 " << product << "这款产品!\n";
}

int main()
{
Company *company = new TechCompany("APPLE","Iphone");//定义TechCompany对象,Company类型 
//指针company指向其地址
TechCompany *techCompany = (TechCompany*)company; //将Company类型强制转换为TechCompany类型    

techCompany->printInfo();

delete company;//释放内存(此处company与techCompany两个指针都指向TechCompany定义的对象) 
//所以释放内存只需要释放一次即可 
company = NULL;
techCompany = NULL;

return 0;
}

实例2:动态强制转换

#include
#include

using namespace std;
class Company
{
public:
Company(string theName,string product);
virtual void printInfo();

protected:
string name;
string product;
};
Company::Company(string theName,string product)
{
name = theName;
this->product = product; 

void Company::printInfo()
{
cout << "这个公司的名字叫:" << name << "正在生产" << product << "\n";
}

class TechCompany:public Company
{
public:
TechCompany(string theName,string product);
virtual void printInfo();
};
TechCompany::TechCompany(string theName,string product):Company(theName,product)
{
}
void TechCompany::printInfo()
{
cout << name << "公司大量生产了 " << product << "这款产品!\n";
}

int main()
{
Company *company = new Company("APPLE","Iphone");//定义Company对象, 
//指针company类型为Company,指向其地址
TechCompany *techCompany = dynamic_cast<TechCompany*>(company); //将Company类型强制转换为TechCompany类型    

if(techCompany != NULL)
{
cout << "成功!\n"; 
}
else
{
cout << "悲催!\n";
}
delete company;//释放内存(此处company与techCompany两个指针都指向TechCompany定义的对象) 
//所以释放内存只需要释放一次即可 
company = NULL;
techCompany = NULL;

return 0;
}

第三十八讲:避免内存泄漏

如果程序会运行很长时间(例如在服务器上,注意不是所有的操作系统都像windows一样每天都要重启),并且在不停地申请新的内存块,忘记释放那些已经不再有用的老内存迟早会把内存消耗殆尽,直接导致后边的new操作无法执行甚至崩溃。

动态内存是没有作用域的,所以必须由程序员来跟踪它们的使用情况,并在不需要用到它们的时候及时把它们归还给系统。虽然动态分配的内存块没有作用域,但用来保存其地址的指针变量是受作用域影响的。

第三十九讲:命名空间和模块化编程

模块化:把程序划分成多个组成部分(即所谓的模块)。这是通过把程序代码分散到多个文件里,等编译程序时再把那些文件重新组合在一起实现的。

C++预处理器的#include指令提供了一种能够让编译器在编译主程序时把其他文件的内容包括进来的机制。例如用这个指令来包括iostream头文件。

实例:头文件应用

Rational.h文件

//Ration.h
//Create by 亦我飞也 

//这个头文件用来声明有理数类(Rational class) 
//类里面对四则运算进行重载,以实现分数运算

#include  

class Rational//定义基类
{
public:
Rational(int num,int denom); //构造器 num = 分子,denom = 分母 

Rational operator+(Rational rhs);// rhs == right hand side(右手边参数) 
Rational operator-(Rational rhs);
Rational operator*(Rational rhs);
Rational operator/(Rational rhs);

private:
void normalize();//负责对分数的简化处理

int numerator; //分子
int denominator; //分母 

friend std::ostream& operator<<(std::ostream& os,Rational f);//传递给它的是哪一个流,它返回的就是那个流的一个引用
}; 
Rational.cpp文件

#include
#include
#include <math.h> 
#include "Rational.h"//系统级别用单尖括号,自定义级别用双引号 

using namespace std;
//class Rational//定义基类
//{
//public:
//    Rational(int num,int denom); //构造器 num = 分子,denom = 分母 
//    
//    Rational operator+(Rational rhs);// rhs == right hand side(右手边参数) 
//    Rational operator-(Rational rhs);
//    Rational operator*(Rational rhs);
//    Rational operator/(Rational rhs);
//    
//private:
//    void normalize();//负责对分数的简化处理
//    
//    int numerator; //分子
//    int denominator; //分母 
//    
//    friend ostream& operator<<(ostream& os,Rational f);//传递给它的是哪一个流,它返回的就是那个流的一个引用
//}; 

Rational::Rational(int num,int denom)//构造函数实现 
{
numerator = num;
denominator = denom;

normalize(); 

//normalize()对分数进行简化操作包括:
//1.只允许分子为负数,如果分母为负数则把负数挪到分子部分,如1/-2==-1/2 
//2.利用欧几里德算法(辗转求余原理)将分数进行简化:2/10 => 1/5 

void Rational::normalize()
{
//确保分母为正
if( denominator < 0) 
{
numerator = -numerator;
denominator = -denominator;
}
//欧几里德算法 
int a = abs(numerator);
int b = abs(denominator);

//求出最大公约数
while(b>0)
{
int t = a % b;//取余 
a = b;
b = t;
}

//分子、分母分别除以最大公约数得到最简化分数
numerator /= a;
denominator /= a; 
}
//a   c   a*d   c*b   a*d + c*d
//- + - = --- + --- = ----------
//b   d   b*d   b*d      b*d
Rational Rational::operator+(Rational rhs)//分数的加运算 
{
int a = numerator;
int b = denominator;
int c = rhs.numerator;
int d = rhs.denominator;

int e = a*b + c*d;
int f = b*d;

return Rational(e,f);
}
//a   c   a   -c
//- - - = - + -- 
//b   d   b   d 
Rational Rational::operator-(Rational rhs)//分数的减运算 
{
rhs.numerator = -rhs.numerator; //被减数分子取负数 
return operator+(rhs);
}
//a   c   a*c
//- * - = --- 

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。 img img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!