C++的新手入门64个答疑
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++的新手入门64个答疑相关的知识,希望对你有一定的参考价值。
基本部分:
1、ctrl+f5 调试不运行,会出现press anykey to continue
f5 调试
2、c++变c,修改Stdafx.h,将#include<stdio.h>替换为#include<iostream>
在主函数源文件中加入using namespace std;
数据类型
3、关于字符型变量。
input:
char a=10;
int b=a+‘\a‘;
output:b=17。
但是字符型的输出不是整数,而是该整数所代表的ASCII码字符。
input:
char a=65;int b=65;
cout<<a<<" "<<b<<endl;
output:
a 65
4、关于常量,可用预处理命令,宏#define,也可用const定义。
#define LIYAKUN 130
const int liYaKun=130;
基本结构:
5、关于main函数
存在int main(int argc,char *argv),arge表示有多少个参数被传递给主函数,argv[]表示参数以字符串数组的形式来传递。
不存在void main(),main函数存在Int型的返回值。
6、输入输出cin<<,cout>>.
cin需要先按下enter键,然后才处理来自键盘的输入。
运算符:
7、C++中存在>=,<=,为关系运算符。
数组:
8、筛选法。可以通过较小的复杂度筛选出两组混杂的数据。
函数:
9、声明要放在头文件中,可以使被调函数与所有声明保持一致,如果函数接口发生变化,只需要修改唯一的声明。
10、形参在函数调用结束后,形参分配的空间即被释放。
11、值传递是无法实现其功能。
void swap(int a,int b)
{
int temp;
temp=a;
a=b;
b=temp;
}
12、int _tmain(int argc, _TCHAR* argv[])可以兼容main()。
13、内联函数inline可以减少函数调用时间,因为inline在编译时,在调用处直接用函数体进行替换。减少了普通函数在掉那样时的栈内存的创建和释放的开销。
但是inline不能用循环、If语句,且必须要简洁。
#include "stdafx.h"
using namespace std;
inline int swap(int a,int b);
inline int swap(int a,int b)
{
int temp;
temp=a;
a=b;
b=temp;
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
cout<<swap(1,2)<<endl;
return 0;
}
指针:
14、指针运算符*,在定义的时候称为指针定义符,此时和指针运算符号的意义完全不同。它的作用是表示所声明的变量的数据类型是一个指针。
int _tmain(int argc, _TCHAR* argv[])
{
int iValue=10;
int *iPtr=&iValue;
cout<<&iPtr<<endl;
cout<<iPtr<<endl;
cout<<*iPtr<<endl;
}
output:
0071F938
0071F938
10
15、指向函数的指针。实际上是指向存储函数代码的首地址。
定义格式如:数据类型 (* 函数指针名)(形参表);
注意:为了与返回指针的函数进行区别,第一个括号不能省略。
在C++中,赋值的格式如:函数指针名(参数表);
注意:函数指针变量不能进行算数运算,毫无意义。
16、动态内存的分配方式。
堆和栈:1)大小。栈是由系统自动分配的连续的地址空间,1M~2M。堆是通过链表来存储的空闲内存地址,受限于系统的虚拟内存,但远远大于栈的内存。2)栈主要是由局部变量,函参。堆是由程序员自行分配的内存区域。因此,栈的空间由系统自动释放,堆的空间由程序员释放。
动态内存的分配:1)可用链表。也就是堆的方式。属于C++的方式。2)可用malloc等函数分配。是自由存储区的方式。属于C的方式。
比较直观的感觉:当进行数组读写时,动态数组的下标可以用变量来表示。
17、C++动态内存的分配。
格式:new 类型名(初始值);如:pnValue=new int(3);//通过New分配了一个存放int类型的内存空间,并且将这个内存上写初始值3。
注意:如果开辟动态内存,一定要判断是否开辟成功。
如:int _tmain(int argc, _TCHAR* argv[])
{
int *pnValue;
pnValue=new int(3);
if (pnValue==NULL) exit(1);//如果内存开辟失败,指针为NULL
else
cout<<"Success!"<<endl;
}
18、C++动态内存的赋值。
当申请空间为数组时,为其赋值不要用指针计算,如“pnValue++;”因为存储地址不是连续的,因此当退回时会出现错误。正确的方法是用过下标或者临时指针来访问动态数组。
下标:int _tmain(int argc, _TCHAR* argv[])
{
int *pnArray=new int[5];
pnArray[0]=1;
pnArray[1]=2;
}
临时指针:int _tmain(int argc, _TCHAR* argv[])
{
int * pnArray=new int[5];
int * pnArrayMove=pnArray;
* pnArrayMove=1;
pnArrayMove++;
* pnArrayMove=2;
}
19、C++初始化动态数组。可用memset函数快速赋值
格式:memset(指针名,初始化值,开辟空间的总字节数)
如:int _tmain(int argc, _TCHAR* argv[])
{
long *plArray=new long[4];
memset(plArray,0,sizeof(long)*5);//sizeof()不能计算动态内存容量
}
20、C++动态内存的释放。
在C中我们用malloce申请,然后用free释放。在C++中我们用new来申请,用delete释放。同样,在释放以后,我们需要把这些指针赋值NULL。
delete a;//a是动态内存变量
delate[] a;//a是动态内存数组
如:int _tmain(int argc, _TCHAR* argv[])
{
long *plArray=new long[10];//申请
meset(plArray,0x00,sizeof(long)*10);//初始化
delete[] plArray;//释放
plArray=NULL;//赋值NULL
}
引用:
21、引用时一个变量或者对象的别名。格式如下:数据类型& 所引用变量或对象名(目标变量或对象)。
如:int _tmain(int argc, _TCHAR* argv[])
{
int nValue;
int& rValue=nValue;
return 0;
}
22、当引用作为函数参数出现时的情况。
在函数被调用时,由于引用函数作为函数参数出现,因此系统在函数体中直接改变实参,这点跟指针的效果一样。这是引用出现的最常见的情况。
如:void swap(int& a,int& b)
{
int temp;
temp=a;
a=b;
b=temp;
}
int _tmain(int argc, _TCHAR* argv[])
{
int nValueA=10;
int nValueB=20;
int& rValueA=nValueA;
int& rValueB=nValueB;
cout<<nValueA<<","<<nValueB<<endl;
swap(rValueA,rValueB);
cout<<rValueA<<","<<rValueB<<endl;
return 0;
}
输出:10,20
20,10
注意:此时,由于函数体改变了引用,。,nValueA=20,nValueB=10。
!! 注意:引用的格式,必须为(类名& 实例名)!空格不能乱加。
!!类名& 函数体(参数),含义是返回一个类的引用。类的实参返回。
共用体:
23、与结构体类型不同的是,共用体的提点1)同一共用体成员共用一个存储区,存储区大小等于最长字节的成员。2)同一时刻,在一个共用体变量中,只有一个成员起作用。
字符串:
24、sizeof()是操作符,用来返回类型的大小,包括\0,strlen()是函数,用来返回字符串的长度,其中不包括\0。
25、cin.getline(数组名称,读取字符数);这个函数读取一行,直至达到换行符,作为字符串的边界。
类:
26、类是C++封装的基本单位,它把数据和函数封装在一起。在定义一个类后,可以声明一个类的变量,即类的对象或者实例。
class 类名
{
…
};
命名类的时候加前缀"C"。
27、在定义类的时候,不为类分配存储空间,不能为类中的数据初始化。
28、成员函数。在类中被声明:
class 类名{
访问控制关键字 返回值类型 成员函数名(参数表);
};
访问控制关键字:public/private(default)/protected,因为如果不对其进行设置,系统会默认设置,所以在定义时需要定义它的访问控制字。
如下:
class math(){
public: //习惯性先设置公共部分
void abs();
void add();
void mul();
private:
string number_1;
string number_2;
};
29、成员函数在类外实现。
class Cmath{
void abs();
}
void Cmath::abs(){
cout<<"Success!"<<endl;
};
30、类的实例,也称对象。当实例为非指针时,访问格式为“实例.类成员”;当实例为指针时,范根格式为“实例指针->类成员”。
如:
math HighMath;
math *pHighMath;
pHighMath=&HighMath;
pHighMath->study();
31、静态数据成员。
当数据在类中声明为private控制型,但在程序过程中需要对它进行修改。此时可以加static来任何类的实例都可以对其数据进行改变。静态数据不属于任何一个实例,只能通过类名来访问。
格式为:int CMath::jingtaishuju=0;
不仅可以在main.cpp中访问,也可以在类实现文件中访问。
32、静态成员函数。
在1)没有实例生成时就需要访问类中的函数信息2)需要所有的类和对象都能访问时,用静态函数。
定义格式:
static 返回值类型 成员函数名(参数表)
访问格式:
类名::成员函数名
构造函数
33、构造函数。构造函数实现在实例被创建时利用特定的值去构造实例,将新建的实例初始化为一个特定状态。
构造函数属于类里面的一个特殊的类,由系统自动调用,定义时无返回值。
格式:
类名();//构造函数名与类名相同
34、带参数的构造函数。带参数的构造函数需要用实参来进行赋值。
!!注意,带参数的构造函数和不带参数的构造函数可以同时存在,相当于构造函数的重载,可8以不带参数赋默认值,也可以带参数,先赋默认值,再赋参数。
所以!最好在每次定义的时候都要写上不带参数的构造函数!
这个地方容易产生很多错误,错例如下:
Cmath xianxingdaishu;
xianxingdaishu.Cmath(1,2,3);
//错误!系统会在第一行语句中默认为,使用默认构造函数,因为构造函数是一直存在的。已经产生了实例以后,这样赋值就是不对的。
如果构造函数要包含参数,就必须在定义的时候给出实参,实参甚至可以不用定义。
改为:
Cmath xianxingdaishu(1,2,3);//此处Cmath不是类名,而是构造函数名。
格式:
类名(初始化参数表);
35、拷贝构造函数。
用一个实例构造另一个实例,使其初始化为与原实例相同的数据,用拷贝构造函数。可以理解为,构造函数为类的一个特殊函数,拷贝构造函数为构造函数的一个特殊例子。拷贝构造函数与构造函数并存。
声明格式:
类名(类名& 实例名)//这里的实例名实际上就是参数名,形参
实现格式:
类名::拷贝构造函数名(类名& 实例参数)//实例参数:形参
36、何时应该声明类头文件?
只在类.cpp和主函数.cpp中声明 类.h。
37、默认拷贝构造函数。
如果不写拷贝构造函数,直接在定义的时候对其进行赋值初始化:
如:
//CMath.h
class CMath{
public:
CMath(string strMathName,string trMathLength,float strMathLevel);//构造函数
//CMath(CMath& MathModel);//拷贝构造函数
void SetMathName();
void SetMathLength();
void SetMathLevel();
void ShowMathName();
void ShowMathLength();
void ShowMathLevel();
private:
string m_strMathName;
string m_strMathLength;
float m_fMathLevel;
};
//CMath.cpp
CMath::CMath(string MathName,string MathLength,float MathLevel){
m_strMathName=MathName;
m_strMathLength=MathLength;
m_fMathLevel=MathLevel;
};
//主函数
#include "stdafx.h"
using namespace std;
#include "CMath.h"
#include "string.h"
int _tmain(int argc, _TCHAR* argv[])
{
CMath MathModel1("FFT","fifty",8);
CMath MathModel2=MathModel1;//调用了默认的拷贝构造函数
MathModel2.ShowMathName();
MathModel2.ShowMathLength();
MathModel2.ShowMathLevel();
}
实际相当于,将所有的非静态变量都赋值给了新定义的实例。
38、默认拷贝构造函数的局限。
1)默认,是完全相同的赋值,实际上很多时候是不必要的。
2)无法实现对动态内存进行拷贝。
39、fatal error LNK1120: 1 个无法解析的外部命令。
因为在头文件已经声明,但是在CPP文件中没有实现。
40、深拷贝。由于存在默认拷贝构造函数的局限性,尤其是在对类中存在动态内存时无法拷贝,深拷贝能完成动态内存的拷贝。
原理,在类中增加深拷贝函数,函数实现中先进行另外一个动态内存申请,然后再赋值。
如:
//CMath.h
class CMath{
public:
CMath(string strMathName,string trMathLength,float strMathLevel);//构造函数
CMath(CMath& MathModel);//拷贝构造函数,深拷贝
void SetMathName();
void SetMathLength();
void SetMathLevel();
void ShowMathName();
void ShowMathLength();
void ShowMathLevel();
private:
string * m_strMathName;//在实现函数中对其进行赋初值,new string
string m_strMathLength;
float m_fMathLevel;
};
//CMath.cpp
CMath::CMath(string MathName,string MathLength,float MathLevel){
m_strMathName=new string;//动态赋初值
*m_strMathName=MathName;
m_strMathLength=MathLength;
m_fMathLevel=MathLevel;
};
CMath::CMath(CMath& MathModel){//建立拷贝构造函数
m_strMathName=new string;//再开动态赋初值
*m_strMathName=*MathModel.m_strMathName;
m_strMathLength=MathModel.m_strMathLength;
m_fMathLevel=MathModel.m_fMathLevel;
};
//主函数
int _tmain(int argc, _TCHAR* argv[])
{
CMath MathModel1("FFT","fifty",8);
CMath MathModel2(MathModel1);//引用已定义的实例
MathModel2.ShowMathName();
MathModel2.ShowMathLength();
MathModel2.ShowMathLevel();
}
41、析构函数。对当前分配的资源进行清理。
声明语法格式:~类名()//virtual ~CMath();//虚析构函数
实现如:CMath::~CMath()
{
delete m_strMathName;
m_strMathName=NULL;
}
注意:析构函数是默认存在的,程序员需要设置对其类中包含的动态变量进行析构。
!!在主程序中不需要调用析构函数,因为系统会在实例的生存期结束以后自动调用类中的析构函数。
!!一旦声明了析构函数,就必须对析构函数进行实现!
42、类的组合。
类的组合其实就是在类的声明里面嵌套其他的类。类的组合主要问题在于初始化,因为要同时对类的内嵌对象进行初始化。
格式:类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表)
{
}
//这个地方还有不清楚的地方。
友元函数和友元类
43、友元就是在需要访问多个类的私有参数时,用到了友元。但是在可以利用关键字friend来修饰。包括友元函数,友元类(类中所有函数都是友元函数)。
声明格式:friend 返回值类型 函数名(参数);
//EleSchStu.h
class EleSchStu{
public:
void SetName(EleSchStu &);
virtual ~EleSchStu();
//设置友元函数来测试名字长度,不一定非要是引用型
friend void CheckNameLength(EleSchStu &);
private:
string strName;
static const int MAX_NAME_LEN;
double score1;
double score2;
double score3;
};
//EleSchStu.cpp
#include "stdafx.h"
using namespace std;
#include "EleSchStu.h"
#include <string>
void EleSchStu::SetName(EleSchStu &xiaoming){
string str;
cout<<"input the name:"<<endl;
cin>>str;
xiaoming.strName=str;
};
EleSchStu::~EleSchStu(){
};
void CheckNameLength(EleSchStu &xiaoming){
if ((xiaoming.strName.length())>xiaoming.MAX_NAME_LEN)
{
cout<<"输入的名字长度太长了!"<<endl;
}
};
//主函数
const int EleSchStu::MAX_NAME_LEN=3;
const int JuniorSchStu::MAX_NAME_LEN=3;
int _tmain(int argc, _TCHAR* argv[])
{
EleSchStu xiaoming;
xiaoming.SetName(xiaoming);
CheckNameLength(xiaoming);
return 0;
}
44、include <string>
重载----重载函数和运算符重载
45、重载函数。允许用同一个函数名定义多个函数,简化程序设计。从而使一个函数名就可以完成一系列相关的任务。
重载函数如下一组:
long abs(long);
double abs(double);
注意!只有是返回类型、参数类型、参数个数、参数顺序上有所不同!不能仅仅是返回值不同。否则不能识别为重载函数。
46、运算符重载。(operator)
对已有的运算符赋予更多的含义。如:使同一个运算符作用于不同类型的数据。(运算符:+,-,*,/,%,new等)
当运算符重载为成员函数时://关键字 operator
格式:函数类型 operator 运算符 (形参表)
{
函数体;
}
为类的友元函数时:
格式: friend 函数体 operator 运算符(形参表)
{
函数体:
}
47、转换运算符的重载。
当用户自定义的数据类型也需要支持数据的转换时,需要用重载转换运算符。而且不能是友元函数。
格式如下:
operator 类型名();//返回类型其实就是类型名,所以不需要制定返回类型。
48、赋值运算符的重载。
关于重载的实例:
需求:写出复数的+、-、*,用类的重载实现,分别用三个友元函数实现运算,能够显示原数据,最终分别用实例来验证三个算法。
//CComplex.h
#include <iostream>
using namespace std;
//CComplex.h
class CComplex
{
public:
CComplex();//不带参数的构造函数
CComplex(double temp_real,double temp_imag);//含参的构造函数
virtual ~CComplex();
void show();
private:
double real;
double imag;
friend CComplex operator + (CComplex a,CComplex b);
friend CComplex operator - (CComplex a,CComplex b);
friend CComplex operator * (CComplex a,CComplex b);
};
//CComplex.cpp
#include <iostream>
using namespace std;
#include "stdafx.h"
#include "CComplex.h"
CComplex::CComplex()
{
cout<<"默认构造函数……"<<endl;
real=0;
imag=0;
}
CComplex::CComplex(double temp_real,double temp_imag)
{
real=temp_real;
imag=temp_imag;
}
CComplex::~CComplex()
{
}
void CComplex::show()
{
cout<<"("<<real<<","<<imag<<")"<<endl;
}
CComplex operator + (CComplex a,CComplex b)
{
CComplex plus;
plus.real=a.real+b.real;
plus.imag=a.imag+b.imag;
return plus;
}
CComplex operator - (CComplex a,CComplex b)
{
CComplex minus;
minus.real=a.real-b.real;
minus.imag=a.imag-b.imag;
return minus;
}
CComplex operator * (CComplex a,CComplex b)
{
CComplex mult;
mult.real=a.real*b.real-a.imag*b.imag;
mult.imag=a.real*b.imag+a.imag*b.real;
return mult;
}
//重载.cpp
#include "stdafx.h"
#include "CComplex.h"
int _tmain(int argc, _TCHAR* argv[])
{
CComplex p1(1.0,2.0);
CComplex p2(3.0,4.0);
CComplex p3,p4,p5;
p3=p1+p2;
p4=p1-p2;
p5=p1*p2;
p1.show();
p2.show();
cout<<"+:";
p3.show();
cout<<"-:";
p4.show();
cout<<"*:";
p5.show();
return 0;
}
继承和派生:
49、派生类。
派生会接收基类中除了构造函数和析构函数以外的所有成员。也可以改造基类成员,对其进行覆盖和重载,也可以增加新的类成员。
格式:
class 派生类名:继承方式 基类名1,继承方式 基类名2…
//!继承只有一个冒号!
{
成员声明;
}
// 继承方式的关键字:public/protected/private(default)
50、继承中的访问控制。
为public继承时,基类中保护成员和公有成员在派生类中不变。
为private继承时,基类中保护成员和公有成员在派生类中变为私有成员。(由于会中止类的继续派生,因此Private继承基本没用)
为protected继承时,基类中保护成员和公有成员在派生类中变为保护成员。(在protected继承中,基类中的protected成员在基类的派生中的访问权限还是protected;在private继承中,基类中的protected成员在基类的派生中访问权限是private,因此在下一级的派生中,就无法访问基类的成员!!因此protected继承更加适合多层派生!!)
!!所有的继承均无法访问基类中的私有成员。
!!!Protected 与Public有区别!!protected是保护的,只有他自身或者继承他的类可以用,public是共有的,在静态下所有类都可以通过类名打点调用,不是静态下,可以用类对象点去调用。
!!!???
如例:
#include "stdafx.h"
//Point.h
class CPoint
{
public:
//CPoint();//构造函数
virtual ~CPoint();
void InitPoint(double x,double y);
double GetX();
double GetY();
protected:
double X,Y;
};
#endif
//Linesegment.h
#include "stdafx.h"
#include "CPoint.h"
class CLinesegment:protected CPoint
{
public:
//CLinesegment();
virtual ~CLinesegment();
void InitLinesegment(double x,double y,double length);
double GetX();//可以直接访问基类的保护成员
double GetY();//可以直接访问基类的保护成员
double GetLength();
protected:
//double X,Y;
double Length;
};
//CPoint.cpp
#include "stdafx.h"
#include "CPoint.h"
//#include "Linesegment.h"
CPoint::~CPoint()
{
};
void CPoint::InitPoint(double x,double y)
{
this->X=x;
this->Y=y;
};
double CPoint::GetX()
{
return this->X;
};
double CPoint::GetY()
{
return this->Y;
};
//Linesegment.cpp
#include "stdafx.h"
#include "CPoint.h"
#include "Linesegment.h"
CLinesegment::~CLinesegment()
{
};
void CLinesegment::InitLinesegment(double x,double y,double length)
{
InitPoint(x,y);//调用基类的函数
this->Length=length;
};
double CLinesegment::GetX()//可以直接访问基类的保护成员?????,因为继承类中不存在x,y的成员变量
{
return this->X;
};
double CLinesegment::GetY()//可以直接访问基类的保护成员?????,因为继承类中不存在x,y的成员变量
{
return this->Y;
};
double CLinesegment::GetLength()
{
return this->Length;
};
//主函数
// 保护继承.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include "CPoint.h"
#include "Linesegment.h"
int _tmain(int argc, _TCHAR* argv[])
{
CLinesegment L1;
L1.InitLinesegment(0,0,5);
cout<<"("<<L1.GetX()<<","<<L1.GetY()<<","<<L1.GetLength()<<")"<<endl;
return 0;
}
51、派生类的构造函数。
由于派生类不能继承基类的构造函数,因此需要定义构造函数。
声明格式: 构造函数(参数);
实现格式:派生类名::派生类名(参数):继承方式1 基类名1(参数表1),继承方式2 基类名2(参数表2)...,内嵌实例名1(参数表1)//内嵌就是在派生类中又定义的类
{
派生类构造函数函数体;
}
!如果基类中没有自定义的构造函数,就将基类名(参数表)省略。所有都可以省略时,可以用生两侧派生类构造函数的成员初始化列表。
52、派生类的析构函数。
直接析构。
53、派生类成员的标识和访问。
格式:派生类实例名.基类名::成员名;
派生类实例名.基类名::成员函数名(函数表);
::是作用域分辨符,可以用于限定要访问的成员所在的类的名称。
!::不可以嵌套使用,比如:实例A.C1::C2::fun();错误!只能在C2函数中声明fun1(){},再调用fun()。
54、虚基类:解决二义性的问题。
声明格式:class 类名: virtual 继承方式 基类名。
实例:定义一个车的基类,有最大速度、质量成员变量,及一些成员函数;派生出自行车类和汽车类,自行车有高度等成员变量,汽车有座位数等成员变量;从自行车和汽车派生出摩托车类。要求观察析构函数的执行和继承中,把车类设成虚基类和不设为虚基类的区别。
//vehicle.h
#ifndef _vehicle_h_
#define _vehicle_h_
#include "stdafx.h"
class CVehicle
{
public:
CVehicle();
CVehicle(int speed,int weight);
virtual ~CVehicle();
protected:
int Speed;
int Weight;
};
#endif
//vehicle.cpp
#include "stdafx.h"
#include "vehicle.h"
CVehicle::CVehicle()
{
cout<<"CVehicle类的实例的构造函数生成!"<<endl;
};
CVehicle::CVehicle(int speed,int weight)
{
this->Speed=speed;
this->Weight=weight;
}
CVehicle::~CVehicle()
{
cout<<"CVehicle类的实例的析构函数生成!"<<endl;
};
//bike.h
#ifndef _bike_H_
#define _bike_H_
#include "stdafx.h"
#include "vehicle.h"
class CBike:virtual public CVehicle
{
public:
CBike();
CBike(int height);
virtual ~CBike();
protected:
int height;
};
#endif
//bike.cpp
#include "stdafx.h"
#include "vehicle.h"
#include "bike.h"
CBike::CBike()
{
cout<<"CBike类的实例的构造函数生成!"<<endl;
};
CBike::CBike(int height)
{
this->height=height;
}
CBike::~CBike()
{
cout<<"CBike类的实例的析构函数生成!"<<endl;
};
//car.h
#ifndef _car_H_
#define _car_H_
#include "stdafx.h"
#include "vehicle.h"
class CCar:virtual protected CVehicle
{
public:
CCar();
CCar(int seat);
virtual ~CCar();
protected:
int seat;
};
#endif
//car.cpp
#include "stdafx.h"
#include "vehicle.h"
#include "car.h"
CCar::CCar()
{
cout<<"CCar类的实例的构造函数生成!"<<endl;
};
CCar::CCar(int seat)
{
this->seat=seat;
};
CCar::~CCar()
{
cout<<"CCar类的实例的析构函数生成!"<<endl;
};
//motor.h
#ifndef _motor_H_
#define _motor_H_
#include "stdafx.h"
#include "vehicle.h"
#include "bike.h"
#include "car.h"
class CMotor: public CBike,public CCar
{
public:
CMotor();
CMotor(int speed, int weight, int height, int seat, int money):CVehicle(speed,weight),CCar(seat),CBike(height)
{
this->money=money;
};
virtual ~CMotor();
void showMotor();
protected:
int money;
};
#endif
//motor.cpp
#include "stdafx.h"
#include "vehicle.h"
#include "car.h"
#include "bike.h"
#include "motor.h"
CMotor::CMotor()
{
cout<<"构造CMotor类的实例!"<<endl;
}
CMotor::~CMotor()
{
cout<<"CMotor类的实例的析构函数生成!"<<endl;
}
void CMotor::showMotor()
{
void showCar();
void showBike();
//showVehicle();
cout<<"speed="<<this->Speed<<endl;
cout<<"weight="<<this->Weight<<endl;
cout<<"height="<<this->height<<endl;
cout<<"seat="<<this->seat<<endl;
cout<<"money="<<this->money<<endl;
}
//主函数
// 继承.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include "vehicle.h"
#include "car.h"
#include "bike.h"
#include "motor.h"
int _tmain(int argc, _TCHAR* argv[])
{
int a=200,b=50000,c=1,d=4,e=100000;
CMotor motor(a,b,c,d,e);
motor.showMotor();
return 0;
}
55、多态性
指同一个函数,根据处理的对象不同,所调用的函数实现不同。通过虚函数来实现。
56、虚函数。
当基类定义的一个函数,派生类对函数进行了重载。重载以后,程序无法确定到底是哪个类调用了函数。
当然,我们可以用作用域运算符来确定到底是哪个调用了。但有的情况下,程序会不知道:声明的函数到底调用了基类的对象,还是派生类的对象。因此,程序会默认调用了基类的对象,导致逻辑错误。
!一句话概括:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。
虚函数定义格式:
virtual 函数返回格式 函数名()
{
};
调用格式():
Class CA
{
public:
…
virtual float xuhanshu()
{};
}
Class CB:public CA
{
public:
...
virtual float xuhanshu()
{};
}
//mian.cpp
float get_xuhanshu(CA& temp)//注意参数类型
{
return temp.xuhanshu();
}
main()
{
CA test1;
CB test2;
get_xuhanshu(test1);//系统识别调用CA中的实例
get_xuhanshu(test2);//系统识别调用CB中的实例
}
57、纯虚函数和抽象类。
纯虚函数就是在一般的虚函数定义的后面加“=0”,本质上是将指向函数体的指针置零。
抽象类是带有纯虚函数的类。抽象类的作用是为了建立类族的共同接口。抽象类不能实例化,但抽象类的派生类可以实例化。
抽象类中的纯虚函数可以在主函数中实现,然后它的引用可以被任何派生类的实例调用。
如:
class A
{
...
virtual void display()=0;
...
}
class B:A
{
...
virtual void display()
{ cout<<"class B"<<endl;};
...
}
class C:B
{
...
virtual void display()
{ cout<<"class C"<<endl;};
...
}
//main.cpp
void display(A& temp)
{
temp.display();
}
void main()
{
B b;
C c;
display(b);//相当于在主函数中声明了一个函数,只要是A类族的参数都可以调用。
display(c);
}
output:
class B
class C
例子:抽象类shape,5个派生类,circle,square,rectangle,trapezoid,triangle,用虚函数分别计算几个图形面积,并求它们的和。用基类指针数组,使它的每一个元素指向一个派生类对象。
//shape.h
#ifndef _Shape_H_
#define _Shape_H_
#include "stdafx.h"
class Shape
{
public:
virtual void show(){};
virtual double area()=0;//只有含返回参数的成员函数,才能当作纯虚函数
//主要当作一个接口
};
#endif
//circle.h
#include "stdafx.h"
#include "shape.h"
#include <math.h>
class circle:public Shape
{
public:
circle(double temp_r)
{
r=temp_r;
};
virtual ~circle(){};
void show()
{
double temp;
temp=area();
cout<<"this is circle! the area is "<<temp<<endl;
};
double area()
{
double temp;
temp=r*r*3.14;
return temp;
};
protected:
double r;
};
//square.h
#include "stdafx.h"
#include "shape.h"
#include <math.h>
class square:public Shape
{
public:
square(double l)
{
this->l=l;
};
virtual ~square(){};
double area()
{
double temp;
temp=this->l*this->l;
return temp;
}
void show()
{
double temp=this->area();
cout<<"this is square!,the area is "<<temp<<endl;
}
protected:
double l;
};
//main.cpp
// 虚函数.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include "shape.h"
#include "circle.h"
#include "square.h"
#include <math.h>
int _tmain(int argc, _TCHAR* argv[])
{
circle c(5);
square s(5);
Shape *p[2];
p[0]=&c;//虚基类的实例可以指向派生类的引用
p[1]=&s;
p[0]->show();
p[1]->show();
return 0;
}
输入输出体系:
58、流。
流其实形象的比喻了C++中数据传输的过程。
流操作包含了许多类。类名包括:ios,istream,ostream...
59、流格式化输出。
如果需要控制流输出的格式,如果要控制流输出的格式。一种用流格式控制符;另一种是利用流类的相关成员函数进行控制。
1)流控制符:
需要定义头文件<iomanip>;
应用如:
int a=15;
cout<<"十进制"<<dec<<a<<endl;//以十进制形式输出整数a
cout<<"十六进制"<<hex<<a<<endl;
double pi=3.14;
cout<<"指数形式"<<setiosflags(ios::scientific)<<setprecision(8);
//不需要记住,只需要知道按指数形式输出,8位小数
2)流控制成员函数。具体应用要具体查文档。
60、文件操作。
首先需要定义输入输出文件流对象头文件<fstream>。
声明对象:
ifstream file_in;//建立输入文件流对象
ofstream file_out;//建立输出文件流对象
fstream file_inout;//建立输入输出文件流对象
构造函数:
ifstream::ifstream(const char*,int=ios::in,int=filebuf::openprot);
ofstream::ofstream(congst char*,int=ios::out,int=filebuf::openprot);
fstream::fstream(cont char*,int,int=filebuf::operprot);
调用构造函数如:
ofstream file_out("C:\\a_out.dat",ios::out|ios::binary);//以二进制方式打开输出文件。
61、检查是是否打开文件。关闭文件。
bool fail();//失败返回true,成功返回false。
类中的成员函数close():
void close();
62、关于写文件。
分为读写字符型文件(主要是读写到.txt)和二进制文件(都写到.dat)。
具体应用如下。
// 打开文件.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <fstream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
//打开文件!
ofstream file_out;//定义打开文件类
file_out.open("C:\\c++.txt",ios::out|ios::app);//打开文件,用于数据输出,从文件中写数据,app是以数据追加方式打开。
if(file_out.fail()) //如果文件打开失败,则显示出错信息。
{
cerr<<"文件 c++.txt 打开失败!"<<endl;
return 1;
}
//输出到文件!
//利用插入操作符进行输出
file_out<<"wo cao ni ma!"<<endl;
file_out<<"我草泥马!"<<endl;
//利用put()进行输出
char a = ‘!‘;
file_out.put(a);
//文件的内容输入到内存!
//利用提取操作符
ifstream file_in;//定义打开文件类
file_in.open("C:\\c++.txt",ios::in);//打开文件,用于数据输入,从文件中读数据)
if(file_in.fail()) //如果文件打开失败,则显示出错信息。
{
cerr<<"文件 c++.txt 打开失败!"<<endl;
return 1;
}
char nRead = 0;
while (!file_in>>nRead)//读取字符,注意,提取的时候是忽略换行和空格字符的
{
cout<<nRead<<" ";//" "即为输出字符
}
while (!file_in.eof())//读取字符,注意,提取的时候是包括换行和空格字符的
//eof()用于判断指针是否已经到文件的末尾了,当返回ture时,已经到达文件的尾部
{
file_in.get(nRead);
cout<<nRead;
}
file_out.close();//关闭文件
file_in.close();
return 0;
}
实例:定义一个结构体,通过键盘输入学生的信息并保存到磁盘stud.dat文件中,并从中读出来显示在屏幕上。
// 二进制文件操作.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
using namespace std;
#include <string>
#include <fstream>
struct Student
{
char name[20];
int age;
char No[20];
char sex[20];
};
int _tmain(int argc, _TCHAR* argv[])
{
Student stu[2];
int i;
int j=0;
for(i=0;i<2;i++)
{
cout<<"please insert your name:";
cin>>stu[i].name;
cout<<"please insert your age:";
cin>>stu[i].age;
cout<<"please insert your Number:";
cin>>stu[i].No;
cout<<"please insert your sex:";
cin>>stu[i].sex;
}
ofstream put_in("C:\\c++.dat",ios::out|ios::binary);//打开二进制文件
if (!put_in) //判断是否打开成功
{
cerr<<"C:\\c++.dat can‘t open it..."<<endl;
exit(1);
}
for(i=0;i<2;i++)
{
put_in.write((char *) &stu[i],sizeof(stu[i]));//文件写入的时候用的是指针,而不是内容
}
put_in.close();
ifstream put_out("C:\\c++.dat",ios::out|ios::binary);//打开二进制文件
if (!put_out)
{
cerr<<"C:\\c++.dat can‘t open it..."<<endl;
exit(1);
}
for(i=0;i<2;i++)
{
put_out.read((char *) &stu[i],sizeof(stu[i]));//成语按函数read()来读二进制文件
}
put_out.close();
for(i=0;i<2;i++) //输出!
{
cout<<"the "<<i+1<<" name:";
cout<<stu[i].name<<endl;
cout<<"the "<<i+1<<" age:";
cout<<stu[i].age<<endl;
cout<<"the "<<i+1<<" Number:";
cout<<stu[i].No<<endl;
cout<<"the "<<i+1<<" sex:";
cout<<stu[i].sex<<endl;
}
return 0;
}
63、异常。
try(){throw 类型名;};
catch(类型变量)
{
};
做过选课系统的应该比较熟悉。
直接上实例。
//MyException.h
#include "stdafx.h"
class CMyException
{
public:
CMyException(string msg)
{
err_msg=msg;
};
virtual ~CMyException()
{
};
void show()
{
cerr<<err_msg<<endl;
}
protected:
string err_msg;
};
//main.cpp
// C++速成.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "MyException.h"
int _tmain(int argc, _TCHAR* argv[])
{
int type;
cout<<"plese insert the number of exception:1.int 2.float 3.double 4.selfdefine"<<endl;
cin>>type;
try{
switch(type)
{
case 4:
throw CsMyException("wrong!");//抛出自定义类异常
break;
case 1:
throw 1;//整形
break;
case 2:
throw 1.2f;//float
break;
case 3:
throw 1.23;//doubule
break;
default:
break;
}
}
catch(CMyException a)
{
a.show();
}
catch(int b)
{
cerr<<"error int!";
}
catch(float c)
{
cerr<<"error float!";
}
catch(double d)
{
cerr<<"error double!";
}
return 0;
}
API编程:
64、API:Windows Application Programming Interface.
windows底层->win32 API函数->windows应用程序
句柄:本身为内存中一个占有4个字长的数值,用于标识应用程序中不同对象和相同对象的不同实例。句柄与普通指针的区别在于,指针包含的是引用对象的内存地址,而句柄则是由系统所管理的引用标识,该标识可以被系统重新定位到一个内存地址上。这种间接访问对象的模式增强了系统对引用对象的控制。
以上是关于C++的新手入门64个答疑的主要内容,如果未能解决你的问题,请参考以下文章