C++的新手入门答疑
基本部分:
、ctrl+f5 调试不运行,会出现press anykey to continue
f5 调试
、c++变c,修改Stdafx.h,将#include<stdio.h>替换为#include<iostream>
在主函数源文件中加入using namespace std; 数据类型
、关于字符型变量。
input:
char a=;
int b=a+'\a';
output:b=。
但是字符型的输出不是整数,而是该整数所代表的ASCII码字符。
input:
char a=;int b=;
cout<<a<<" "<<b<<endl;
output:
a
、关于常量,可用预处理命令,宏#define,也可用const定义。
#define LIYAKUN 130
const int liYaKun=; 基本结构:
、关于main函数
存在int main(int argc,char *argv),arge表示有多少个参数被传递给主函数,argv[]表示参数以字符串数组的形式来传递。
不存在void main(),main函数存在Int型的返回值。
、输入输出cin<<,cout>>.
cin需要先按下enter键,然后才处理来自键盘的输入。 运算符:
、C++中存在>=,<=,为关系运算符。 数组:
、筛选法。可以通过较小的复杂度筛选出两组混杂的数据。 函数: 、声明要放在头文件中,可以使被调函数与所有声明保持一致,如果函数接口发生变化,只需要修改唯一的声明。
、形参在函数调用结束后,形参分配的空间即被释放。
、值传递是无法实现其功能。
void swap(int a,int b)
{
int temp;
temp=a;
a=b;
b=temp;
}
、int _tmain(int argc, _TCHAR* argv[])可以兼容main()。
、内联函数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 ;
} int _tmain(int argc, _TCHAR* argv[])
{
cout<<swap(,)<<endl;
return ;
} 指针:
、指针运算符*,在定义的时候称为指针定义符,此时和指针运算符号的意义完全不同。它的作用是表示所声明的变量的数据类型是一个指针。
int _tmain(int argc, _TCHAR* argv[])
{
int iValue=;
int *iPtr=&iValue;
cout<<&iPtr<<endl;
cout<<iPtr<<endl;
cout<<*iPtr<<endl;
}
output:
0071F938
0071F938 、指向函数的指针。实际上是指向存储函数代码的首地址。
定义格式如:数据类型 (* 函数指针名)(形参表);
注意:为了与返回指针的函数进行区别,第一个括号不能省略。
在C++中,赋值的格式如:函数指针名(参数表);
注意:函数指针变量不能进行算数运算,毫无意义。
、动态内存的分配方式。
堆和栈:)大小。栈是由系统自动分配的连续的地址空间,1M~2M。堆是通过链表来存储的空闲内存地址,受限于系统的虚拟内存,但远远大于栈的内存。)栈主要是由局部变量,函参。堆是由程序员自行分配的内存区域。因此,栈的空间由系统自动释放,堆的空间由程序员释放。
动态内存的分配:)可用链表。也就是堆的方式。属于C++的方式。)可用malloc等函数分配。是自由存储区的方式。属于C的方式。 比较直观的感觉:当进行数组读写时,动态数组的下标可以用变量来表示。
、C++动态内存的分配。
格式:new 类型名(初始值);如:pnValue=new int();//通过New分配了一个存放int类型的内存空间,并且将这个内存上写初始值3。
注意:如果开辟动态内存,一定要判断是否开辟成功。
如:int _tmain(int argc, _TCHAR* argv[])
{
int *pnValue;
pnValue=new int();
if (pnValue==NULL) exit();//如果内存开辟失败,指针为NULL
else
cout<<"Success!"<<endl;
}
、C++动态内存的赋值。
当申请空间为数组时,为其赋值不要用指针计算,如“pnValue++;”因为存储地址不是连续的,因此当退回时会出现错误。正确的方法是用过下标或者临时指针来访问动态数组。
下标:int _tmain(int argc, _TCHAR* argv[])
{
int *pnArray=new int[];
pnArray[]=;
pnArray[]=;
}
临时指针:int _tmain(int argc, _TCHAR* argv[])
{
int * pnArray=new int[];
int * pnArrayMove=pnArray;
* pnArrayMove=;
pnArrayMove++;
* pnArrayMove=;
}
、C++初始化动态数组。可用memset函数快速赋值
格式:memset(指针名,初始化值,开辟空间的总字节数)
如:int _tmain(int argc, _TCHAR* argv[])
{
long *plArray=new long[];
memset(plArray,,sizeof(long)*);//sizeof()不能计算动态内存容量
}
、C++动态内存的释放。
在C中我们用malloce申请,然后用free释放。在C++中我们用new来申请,用delete释放。同样,在释放以后,我们需要把这些指针赋值NULL。
delete a;//a是动态内存变量
delate[] a;//a是动态内存数组
如:int _tmain(int argc, _TCHAR* argv[])
{
long *plArray=new long[];//申请
meset(plArray,0x00,sizeof(long)*);//初始化 delete[] plArray;//释放
plArray=NULL;//赋值NULL
} 引用:
、引用时一个变量或者对象的别名。格式如下:数据类型& 所引用变量或对象名(目标变量或对象)。
如:int _tmain(int argc, _TCHAR* argv[])
{
int nValue;
int& rValue=nValue;
return ;
}
、当引用作为函数参数出现时的情况。
在函数被调用时,由于引用函数作为函数参数出现,因此系统在函数体中直接改变实参,这点跟指针的效果一样。这是引用出现的最常见的情况。
如:void swap(int& a,int& b)
{
int temp;
temp=a;
a=b;
b=temp;
} int _tmain(int argc, _TCHAR* argv[])
{
int nValueA=;
int nValueB=;
int& rValueA=nValueA;
int& rValueB=nValueB;
cout<<nValueA<<","<<nValueB<<endl;
swap(rValueA,rValueB);
cout<<rValueA<<","<<rValueB<<endl;
return ;
}
输出:,
,
注意:此时,由于函数体改变了引用,。,nValueA=,nValueB=。
!! 注意:引用的格式,必须为(类名& 实例名)!空格不能乱加。
!!类名& 函数体(参数),含义是返回一个类的引用。类的实参返回。 共用体:
、与结构体类型不同的是,共用体的提点1)同一共用体成员共用一个存储区,存储区大小等于最长字节的成员。)同一时刻,在一个共用体变量中,只有一个成员起作用。 字符串:
、sizeof()是操作符,用来返回类型的大小,包括\,strlen()是函数,用来返回字符串的长度,其中不包括\。
、cin.getline(数组名称,读取字符数);这个函数读取一行,直至达到换行符,作为字符串的边界。 类:
、类是C++封装的基本单位,它把数据和函数封装在一起。在定义一个类后,可以声明一个类的变量,即类的对象或者实例。
class 类名
{
…
};
命名类的时候加前缀"C"。
、在定义类的时候,不为类分配存储空间,不能为类中的数据初始化。
、成员函数。在类中被声明:
class 类名{
访问控制关键字 返回值类型 成员函数名(参数表);
};
访问控制关键字:public/private(default)/protected,因为如果不对其进行设置,系统会默认设置,所以在定义时需要定义它的访问控制字。
如下:
class math(){
public: //习惯性先设置公共部分
void abs();
void add();
void mul(); private:
string number_1;
string number_2;
};
、成员函数在类外实现。
class Cmath{
void abs();
} void Cmath::abs(){
cout<<"Success!"<<endl;
};
、类的实例,也称对象。当实例为非指针时,访问格式为“实例.类成员”;当实例为指针时,范根格式为“实例指针->类成员”。
如:
math HighMath;
math *pHighMath;
pHighMath=&HighMath;
pHighMath->study();
、静态数据成员。
当数据在类中声明为private控制型,但在程序过程中需要对它进行修改。此时可以加static来任何类的实例都可以对其数据进行改变。静态数据不属于任何一个实例,只能通过类名来访问。
格式为:int CMath::jingtaishuju=;
不仅可以在main.cpp中访问,也可以在类实现文件中访问。
、静态成员函数。
在1)没有实例生成时就需要访问类中的函数信息2)需要所有的类和对象都能访问时,用静态函数。
定义格式:
static 返回值类型 成员函数名(参数表)
访问格式:
类名::成员函数名 构造函数
、构造函数。构造函数实现在实例被创建时利用特定的值去构造实例,将新建的实例初始化为一个特定状态。
构造函数属于类里面的一个特殊的类,由系统自动调用,定义时无返回值。
格式:
类名();//构造函数名与类名相同
、带参数的构造函数。带参数的构造函数需要用实参来进行赋值。
!!注意,带参数的构造函数和不带参数的构造函数可以同时存在,相当于构造函数的重载,可8以不带参数赋默认值,也可以带参数,先赋默认值,再赋参数。 所以!最好在每次定义的时候都要写上不带参数的构造函数! 这个地方容易产生很多错误,错例如下:
Cmath xianxingdaishu;
xianxingdaishu.Cmath(,,);
//错误!系统会在第一行语句中默认为,使用默认构造函数,因为构造函数是一直存在的。已经产生了实例以后,这样赋值就是不对的。
如果构造函数要包含参数,就必须在定义的时候给出实参,实参甚至可以不用定义。
改为:
Cmath xianxingdaishu(,,);//此处Cmath不是类名,而是构造函数名。
格式:
类名(初始化参数表);
、拷贝构造函数。
用一个实例构造另一个实例,使其初始化为与原实例相同的数据,用拷贝构造函数。可以理解为,构造函数为类的一个特殊函数,拷贝构造函数为构造函数的一个特殊例子。拷贝构造函数与构造函数并存。
声明格式:
类名(类名& 实例名)//这里的实例名实际上就是参数名,形参
实现格式:
类名::拷贝构造函数名(类名& 实例参数)//实例参数:形参
、何时应该声明类头文件?
只在类.cpp和主函数.cpp中声明 类.h。
、默认拷贝构造函数。
如果不写拷贝构造函数,直接在定义的时候对其进行赋值初始化:
如:
//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",);
CMath MathModel2=MathModel1;//调用了默认的拷贝构造函数
MathModel2.ShowMathName();
MathModel2.ShowMathLength();
MathModel2.ShowMathLevel();
}
实际相当于,将所有的非静态变量都赋值给了新定义的实例。
、默认拷贝构造函数的局限。
)默认,是完全相同的赋值,实际上很多时候是不必要的。
)无法实现对动态内存进行拷贝。
、fatal error LNK1120: 个无法解析的外部命令。
因为在头文件已经声明,但是在CPP文件中没有实现。
、深拷贝。由于存在默认拷贝构造函数的局限性,尤其是在对类中存在动态内存时无法拷贝,深拷贝能完成动态内存的拷贝。
原理,在类中增加深拷贝函数,函数实现中先进行另外一个动态内存申请,然后再赋值。
如:
//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",);
CMath MathModel2(MathModel1);//引用已定义的实例
MathModel2.ShowMathName();
MathModel2.ShowMathLength();
MathModel2.ShowMathLevel();
}
、析构函数。对当前分配的资源进行清理。
声明语法格式:~类名()//virtual ~CMath();//虚析构函数
实现如:CMath::~CMath()
{
delete m_strMathName;
m_strMathName=NULL;
}
注意:析构函数是默认存在的,程序员需要设置对其类中包含的动态变量进行析构。
!!在主程序中不需要调用析构函数,因为系统会在实例的生存期结束以后自动调用类中的析构函数。
!!一旦声明了析构函数,就必须对析构函数进行实现!
、类的组合。
类的组合其实就是在类的声明里面嵌套其他的类。类的组合主要问题在于初始化,因为要同时对类的内嵌对象进行初始化。
格式:类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表)
{
}
//这个地方还有不清楚的地方。 友元函数和友元类
、友元就是在需要访问多个类的私有参数时,用到了友元。但是在可以利用关键字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=;
const int JuniorSchStu::MAX_NAME_LEN=;
int _tmain(int argc, _TCHAR* argv[])
{
EleSchStu xiaoming;
xiaoming.SetName(xiaoming);
CheckNameLength(xiaoming);
return ;
} 、include <string> 重载----重载函数和运算符重载
、重载函数。允许用同一个函数名定义多个函数,简化程序设计。从而使一个函数名就可以完成一系列相关的任务。
重载函数如下一组:
long abs(long);
double abs(double);
注意!只有是返回类型、参数类型、参数个数、参数顺序上有所不同!不能仅仅是返回值不同。否则不能识别为重载函数。
、运算符重载。(operator)
对已有的运算符赋予更多的含义。如:使同一个运算符作用于不同类型的数据。(运算符:+,-,*,/,%,new等)
当运算符重载为成员函数时://关键字 operator
格式:函数类型 operator 运算符 (形参表)
{
函数体;
}
为类的友元函数时:
格式: friend 函数体 operator 运算符(形参表)
{
函数体:
}
、转换运算符的重载。
当用户自定义的数据类型也需要支持数据的转换时,需要用重载转换运算符。而且不能是友元函数。
格式如下:
operator 类型名();//返回类型其实就是类型名,所以不需要制定返回类型。
、赋值运算符的重载。
关于重载的实例:
需求:写出复数的+、-、*,用类的重载实现,分别用三个友元函数实现运算,能够显示原数据,最终分别用实例来验证三个算法。
//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=;
imag=;
}
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 ;
} 继承和派生:
、派生类。
派生会接收基类中除了构造函数和析构函数以外的所有成员。也可以改造基类成员,对其进行覆盖和重载,也可以增加新的类成员。
格式:
class 派生类名:继承方式 基类名1,继承方式 基类名2…
//!继承只有一个冒号!
{
成员声明;
}
// 继承方式的关键字:public/protected/private(default)
、继承中的访问控制。 为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(,,);
cout<<"("<<L1.GetX()<<","<<L1.GetY()<<","<<L1.GetLength()<<")"<<endl;
return ;
}
、派生类的构造函数。
由于派生类不能继承基类的构造函数,因此需要定义构造函数。
声明格式: 构造函数(参数);
实现格式:派生类名::派生类名(参数):继承方式1 基类名1(参数表1),继承方式2 基类名2(参数表2)...,内嵌实例名1(参数表1)//内嵌就是在派生类中又定义的类
{
派生类构造函数函数体;
}
!如果基类中没有自定义的构造函数,就将基类名(参数表)省略。所有都可以省略时,可以用生两侧派生类构造函数的成员初始化列表。
、派生类的析构函数。
直接析构。
、派生类成员的标识和访问。
格式:派生类实例名.基类名::成员名;
派生类实例名.基类名::成员函数名(函数表);
::是作用域分辨符,可以用于限定要访问的成员所在的类的名称。
!::不可以嵌套使用,比如:实例A.C1::C2::fun();错误!只能在C2函数中声明fun1(){},再调用fun()。
、虚基类:解决二义性的问题。
声明格式: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=,b=,c=,d=,e=;
CMotor motor(a,b,c,d,e);
motor.showMotor();
return ;
}
、多态性
指同一个函数,根据处理的对象不同,所调用的函数实现不同。通过虚函数来实现。
、虚函数。
当基类定义的一个函数,派生类对函数进行了重载。重载以后,程序无法确定到底是哪个类调用了函数。
当然,我们可以用作用域运算符来确定到底是哪个调用了。但有的情况下,程序会不知道:声明的函数到底调用了基类的对象,还是派生类的对象。因此,程序会默认调用了基类的对象,导致逻辑错误。
!一句话概括:在基类的函数前加上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中的实例
} 、纯虚函数和抽象类。
纯虚函数就是在一般的虚函数定义的后面加“=”,本质上是将指向函数体的指针置零。
抽象类是带有纯虚函数的类。抽象类的作用是为了建立类族的共同接口。抽象类不能实例化,但抽象类的派生类可以实例化。
抽象类中的纯虚函数可以在主函数中实现,然后它的引用可以被任何派生类的实例调用。
如:
class A
{
...
virtual void display()=;
...
}
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()=;//只有含返回参数的成员函数,才能当作纯虚函数
//主要当作一个接口
};
#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();
square s();
Shape *p[];
p[]=&c;//虚基类的实例可以指向派生类的引用
p[]=&s;
p[]->show();
p[]->show();
return ;
}
输入输出体系:
、流。
流其实形象的比喻了C++中数据传输的过程。
流操作包含了许多类。类名包括:ios,istream,ostream...
、流格式化输出。
如果需要控制流输出的格式,如果要控制流输出的格式。一种用流格式控制符;另一种是利用流类的相关成员函数进行控制。
)流控制符:
需要定义头文件<iomanip>;
应用如:
int a=;
cout<<"十进制"<<dec<<a<<endl;//以十进制形式输出整数a
cout<<"十六进制"<<hex<<a<<endl;
double pi=3.14;
cout<<"指数形式"<<setiosflags(ios::scientific)<<setprecision();
//不需要记住,只需要知道按指数形式输出,8位小数
)流控制成员函数。具体应用要具体查文档。
、文件操作。
首先需要定义输入输出文件流对象头文件<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);//以二进制方式打开输出文件。
、检查是是否打开文件。关闭文件。
bool fail();//失败返回true,成功返回false。
类中的成员函数close():
void close();
、关于写文件。
分为读写字符型文件(主要是读写到.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 ;
} //输出到文件!
//利用插入操作符进行输出
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 ;
} char nRead = ;
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 ;
}
实例:定义一个结构体,通过键盘输入学生的信息并保存到磁盘stud.dat文件中,并从中读出来显示在屏幕上。
// 二进制文件操作.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
using namespace std;
#include <string>
#include <fstream> struct Student
{
char name[];
int age;
char No[];
char sex[];
};
int _tmain(int argc, _TCHAR* argv[])
{
Student stu[];
int i;
int j=;
for(i=;i<;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();
}
for(i=;i<;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();
}
for(i=;i<;i++)
{
put_out.read((char *) &stu[i],sizeof(stu[i]));//成语按函数read()来读二进制文件
}
put_out.close();
for(i=;i<;i++) //输出!
{
cout<<"the "<<i+<<" name:";
cout<<stu[i].name<<endl;
cout<<"the "<<i+<<" age:";
cout<<stu[i].age<<endl;
cout<<"the "<<i+<<" Number:";
cout<<stu[i].No<<endl;
cout<<"the "<<i+<<" sex:";
cout<<stu[i].sex<<endl;
}
return ;
} 、异常。
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 :
throw CsMyException("wrong!");//抛出自定义类异常
break;
case :
throw ;//整形
break;
case :
throw 1.2f;//float
break;
case :
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 ;
}
API编程:
、API:Windows Application Programming Interface.
windows底层->win32 API函数->windows应用程序
句柄:本身为内存中一个占有4个字长的数值,用于标识应用程序中不同对象和相同对象的不同实例。句柄与普通指针的区别在于,指针包含的是引用对象的内存地址,而句柄则是由系统所管理的引用标识,该标识可以被系统重新定位到一个内存地址上。这种间接访问对象的模式增强了系统对引用对象的控制。
C++的新手入门答疑的更多相关文章
- 新手入门指导:Vue 2.0 的建议学习顺序
起步 1. 扎实的 JavaScript / HTML / CSS 基本功.这是前置条件. 2. 通读官方教程 (guide) 的基础篇.不要用任何构建工具,就只用最简单的 <script> ...
- Flume NG Getting Started(Flume NG 新手入门指南)
Flume NG Getting Started(Flume NG 新手入门指南)翻译 新手入门 Flume NG是什么? 有什么改变? 获得Flume NG 从源码构建 配置 flume-ng全局选 ...
- 原创:从零开始,微信小程序新手入门宝典《一》
为了方便大家了解并入门微信小程序,我将一些可能会需要的知识,列在这里,让大家方便的从零开始学习:一:微信小程序的特点张小龙:张小龙全面阐述小程序,推荐通读此文: 小程序是一种不需要下载.安装即可使用的 ...
- 【原创】新手入门一篇就够:从零开发移动端IM
一.前言 IM发展至今,已是非常重要的互联网应用形态之一,尤其移动互联网时代,它正以无与论比的优势降低了沟通成本和沟通代价,对各种应用形态产生了深远影响. 做为IM开发者或即将成为IM开发者的技术人员 ...
- 课程上线 -“新手入门 : Windows Phone 8.1 开发”
经过近1个月的准备和录制,“新手入门 : Windows Phone 8.1 开发”系列课程已经在Microsoft 虚拟学院上线,链接地址为:http://www.microsoftvirtuala ...
- WordPress建站 新手入门
WordPress建站 新手入门教程系列 1. WordPress入门 之 什么是WordPress? 2. WordPress入门 之 搭建WordPress站点需要什么条件? 3. WordPre ...
- 安卓自动化测试(2)Robotium环境搭建与新手入门教程
Robotium环境搭建与新手入门教程 准备工具:Robotium资料下载 知识准备: java基础知识,如基本的数据结构.语法结构.类.继承等 对Android系统较为熟悉,了解四大组件,会编写简单 ...
- mongodb新手入门,mongodb命令学习
下面来总结一下mongodb新手入门的常用命令吧.要是您是mongodb新手,可以看下. 1,show dbs 查询mongodb里面的数据库列表 如果想查看当前连接在哪个数据库下面,可以直接输入db ...
- iOS简易柱状图(带动画)--新手入门篇
叨逼叨 好久没更新博客了,才几个月,发生了好多事情,处理了好多事情.不变的是写代码依然在继续. 做点啥子 看看objective-c的书,学着写了个柱状图,只是练习的demo而已,iOS上的图表控件已 ...
随机推荐
- api.versioning 版本控制 自动识别最高版本
Microsoft.AspNetCore.Mvc.Versioning //引入程序集 .net core 下面api的版本控制作用不需要多说,可以查阅https://www.cnblogs.com/ ...
- JAVASE(十八) 反射: Class的获取、ClassLoader、反射的应用、动态代理
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1.反射(JAVA Reflection)的理解 1.1 什么是反射(JAVA Reflection) ...
- Java实现 蓝桥杯 算法提高 求arccos值
算法提高 7-2求arccos值 时间限制:10.0s 内存限制:256.0MB 提交此题 问题描述 利用标准库中的cos(x)和fabs(x)函数实现arccos(x)函数,x取值范围是[-1, 1 ...
- Java实现 LeetCode 345 反转字符串中的元音字母
345. 反转字符串中的元音字母 编写一个函数,以字符串作为输入,反转该字符串中的元音字母. 示例 1: 输入: "hello" 输出: "holle" 示例 ...
- java实现滑动解锁
滑动解锁是智能手机一项常用的功能.你需要在3x3的点阵上,从任意一个点开始,反复移动到一个尚未经过的"相邻"的点.这些划过的点所组成的有向折线,如果与预设的折线在图案.方向上都一致 ...
- java实现蔬菜价格计算
** 蔬菜价格计算** 计算蔬菜总价 为了丰富群众菜篮子,平抑菜价,相关部分组织了蔬菜的调运.今某箱中有多个品种的蔬菜.蔬菜的单价(元/公斤)存放在price数组中,蔬菜的重量(公斤)存放在weigh ...
- tensorflow2.0学习笔记第一章第五节
1.5简单神经网络实现过程全览
- (二)linux三剑客之awk
1.awk是什么和上一节的grep有什么区别? 2.awk解决了哪些问题? 3.awk的工作原理? 4.awk的基础用法? 5.awk技术常用[收藏] 1.awk是什么? awk 用于处理文本,gre ...
- JVM性能优化 (一) 初识JVM
一.我们为什么要对JVM做优化 在本地开发环境中我们很少会遇到需要对JVM进行优化的需求,但是到了生产环境,我们可能会有下面的需求: 运行的应用"卡住了",日志不输出,程序没有反应 ...
- Canvas绘制圆点线段
最近一个小伙遇到一个需求,客户需要绘制圆点样式的线条. 大致效果是这样的: 思路一:计算并使用arc填充 他自己实现了一种思路,然后咨询我有没有更好的思路. 先看看他的思路是如何实现的,大致代码如下: ...