为了完成教学中的项目,需要了解一些C++的知识,也需要知道它和C的一些区别。
现在开始看一些它们的区别吧
1、首先在C++中,malloc是返回值是void
所以char *p=malloc(100);
在C++中无法使用,需要强转:char *p=(char *)malloc(100)
其实在C++很多时候经常用 new,是C++的一个关键字,不需要包含任何头文件
char *p=new char[100];//在堆中分配100个char
delete []p;//释放一个数组,如果只有p那么只释放第一个char
char *p1=new char;//在堆中分配一个char
delete p1;//释放一个char
//c++语言可以兼容c语言的内存管理机制,但如果用malloc分配的内存就必须要free
如果用new分配的就必须用delete,不能混合使用
2、在c语言中,可用下面的方法绕过const
const int a=10;
int *p2=(int *)&a;
*p2=100;
printf("%d\n",a);
但在C++中不会修改a的值
3、在c语言中
void test (int a=10);//c语言中是非法的,C++是合法的,此时调用的时候还是根据输入的实参调用这个函数,但是如果缺省函数的实参,就会使用a=10.这个叫形参的缺省值
{
}
void test (int a,int b);
{
}
//在C语言中,这种全局的函数名字不允许相同,所以这是非法的,但在C++中是合法的,这叫重载,根据调用的时候参数的不同,自动调用相应的函数
4、当然最主要的区别还是C++是面向对象的= =
#include <stdio.h>
#include <string.h>
struct man
{
char name[20];
int age;
void set_name(const char *p)//在结构体中可以使用函数
{
strcpy(name, p);
}
void set_age(int a)
{
age = a;
}
const char *get_name()
{
return name;//在结构体中,它就不在内存中,具体在堆在栈中应该看定义它的语法
}
int get_age()
{
return age;
}
man()//没有返回值,名字和结构名字相同,这叫构造函数
{
printf("hello world\n");
}
man(const char *p, int a = 0)//构造函数的重载
{
age = a;
strcpy(name, p);
}
~man()//析构函数
{
printf("~man\n");
}
};
int main01()
{
//man m;//前面不用加struct
//m.set_name("Tom");//C++一般这样设置,而不是在这里调用strcpy
// m.set_age(20);
// printf("%s, %d\n", m.get_name(), m.get_age());//虽然m在栈中,但此时m并未消失,m.get_name()地址是有效的
//如果想把m放到堆中
man *m = new man;//这里结构成了堆里面一个变量
m->set_name("tom");//因为m是指针,所以需要改成->
m->set_age(20);
printf("%s, %d\n", m->get_name(), m->get_age());
delete m;
man m1;//结构成了栈里面一个变量了,这里调用的是没有参数的构造函数
man m2("abc", 100);// 明确的调用有const char *参数的构造函数
printf("%s, %d\n", m2.get_name(), m2.get_age());
man *m3 = new man("张三", 30);
printf("%s, %d\n", m3->get_name(), m3->get_age());
delete m3;
return 0;
}
struct A//这里分析构造和析构的行为
{
int age;
A(int a)
{
age = a;
printf("A, %d\n", age);
}
~A()
{
printf("~A, %d\n", age);
}
};
A a;//通过这样达到在main前执行代码
int main()
{
printf("hell world\n");//如何在调用main函数之前就执行一段代码?
A a(10);
A *p = new A(20);
delete p;
return 0;
}
构造函数是一个结构在内存中变成一个变量的时候,自动被调用的
在内存中出现的时候,自动调用构造函数,如果在内存中销毁的时候自动调用析构函数
构造函数可以有多个,但析构函数只能有一个,而且没有参数
5、this指针
#include <stdio.h>
struct A
{
A()
{
//int a;
//scanf("%d", &a);
//int b;
//scanf("%d", &b);
//printf("%d\n", a + b);
}
int age;
void set_age(int age)
{
this->age = age;//this是c++的关键字,代表对象的地址
}
struct A *get_addr()
{
return this;
}
};
int main()
{
A a;//a是一个对象,
printf("%p, %p\n", &a, a.get_addr());
A *p = new A;
printf("%p, %p\n", p, p->get_addr());
delete p;
return 0;
}
类与结构体的区别
C++里面,结构所有的成员都是公有的,就是外部可以访问的
class里面所有的成员都是私有的,外部不能访问
public:公有成员
private:私有成员
#include <stdio.h>
#include <string.h>
class man
{
public:
char *name;
man()//给下面的if(name)用
{
name = NULL;
}
void set_name(const char *name)
{
if (name)//如果不加这句会隐含内存泄露,但是刚开始没有name可以删除,所以小技巧是在上面加一个构造函数
delete []this->name;
this->name = new char[strlen(name) + 1];//在外面使用时就不用关心内存了,且注意此处的this
strcpy(this->name, name);
}
const char *get_name()
{
if (name)
return name;
else
return "nothing";
}
int get_age()
{
return age;//可以通过这种方法,访问到age
}
~man()
{
if (name)//不加这句的话,如果后面没有对name初始化,这里也会有问题,所以加if(name)
delete []name;
}
private:
int age; //此时在main函数里面访问它,是不能访问的
};
int main()
{
man *m = new man;
m->set_name("sdfsdfsdfsdfdsfsdfsdfdsfsdfsfdsf");
m->set_name("a");//如果不在select中加delete会产生内存泄露
printf("%s\n", m->get_name());
printf("%d\n", m->get_age());
delete m;
return 0;
}
#include <stdio.h>
#include <string.h>
class man
{
private:
char *name;
int age;
public:
man()
{
name = NULL;
age = 0;
}
~man()
{
if (name)
delete []name;
}
const char *get_name()
{
return name;
}
int get_age()
{
return age;
}
void set_name(const char *name)
{
if (strcmp(name, "凤姐") == 0)//之所以用private,就像这样,可以对成员进行限制
return;
if (this->name)
delete []this->name;
this->name = new char[strlen(name) + 1];
strcpy(this->name ,name);
}
void set_age(int age)
{
if (age >= 0 && age <= 200)//之所以用private,就像这样,可以对成员进行限制
this->age = age;
}
};
int main()
{
man m;
// m.set_name("Tom");
// m.set_age("30");
char name[100];
scanf("%s", name);
int age= 0;
scanf("%d", &age);
m.set_name(name);//通过用户输入指定名字
m.set_age(age);
printf("%s, %d\n", m.get_name(), m.get_age());
return 0;
}
类的继承
父类和子类间有关联
例子(不是类的例子,是个破解按键的有趣的例子)
在VS中新建一个MFC项目
可以设置按键等,也可以设置某些键位不能使用,此时可以对其破解。
windows的窗口都有句柄,窗口里的控件也一样有句柄
先得到句柄
在VS工具栏下选择spy++(64)
在弹出的窗口选择会再弹出一个窗口,此时将拖到窗口的按键上,handle窗口就会显示出它的句柄。此时得到0x000607B2
新建一个控制台项目,用c语言写,包含
#include <Windows.h>
int main()
{
Sleep(3000);//等待3S
SendMessage((HWND)0x000607B2, WM_LBUTTONDOWN, 0, 0);//给指定的句柄发送消息,发送一个鼠标左键按下消息 HWND是把数字强转为句柄
SendMessage((HWND)0x000607B2, WM_LBUTTONUP, 0, 0);//给指定的句柄发送消息,发送一个鼠标左键抬起的消息
return 0;
}
此时可以发现打开的程序中,原来不能按的按钮(虽然还是不能按),但其按下去产生的结果出现了
IDE实例
在VS中创建一个MFC工程,
选择基于对话框
后面框架样式选择最大化框,最小化框,然后一直next到完成,此时可以得到一个可编辑的图形的界面。
把上面所有的按钮删除,并从左边
的Toolbox中选择Edit Control放到界面中,可以调整它的大小,然后在右下角调整它的属性:
将Want Return 和Multiline调整为true,分别代表回车换行和允许多行输入。此时可以F5试着运行一下。
可以在空白的编辑框中输入文字,并且可以输入多行,但是可以发现,当拖动它的边框,调整外面边框的大小后,内部的小编辑框大小不会改变,且没有拖动条。需要进一步改进
首先是滚动条
将Horizontal/Vertical Scroll调成True可以发现编辑界面多了滚动条如图
第二呢就是把这个空白的编辑窗口弄得和窗口一样大。
右键这个窗口,添加变量,类型control ,起个名字edit1,那么完成之后,可以发现ideDlg.h文件中多了一行
public:
afx_msg void OnBnClickedOk();
afx_msg void OnEnChangeEdit1();
CEdit edit1;//多出来的代码
然后去它的cpp代码中,去添加代码,实现编辑窗口和实际的窗口一样大
void CIDEDlg::OnPaint()//窗口大小发生改变的时候会调用它
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CRect rect;//MFC提供类,实现了一个矩形结构
GetClientRect(&rect);//得到窗口客户区大小
edit1.SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(), 0);//设置edit1的位置和大小(0,0)指的是相对窗口的坐标 这三行代码实现了编辑窗和实际窗口大小相同
CDialogEx::OnPaint();
}
}
当窗口变化时,就会调用上面这个函数,通过添加三行函数就可以实现了
下面给它添加一个菜单,右键资源管理文件夹,选择添加,添加新的资源,再选menu就可以打开一个菜单编辑器
现在来编辑里面的内容
此时这些菜单选项是没有用的。返回到对话框那里,发现对话框属性里的menu,把它改成刚刚编辑的菜单名字
再F5运行一下
得到如下结果:
下一步,给菜单栏添加一些功能,首先回到菜单项,右键菜单中的退出,选择添加事件
注意修改类和消息类型
点Add and Edit后,发现进入代码编辑界面,也就是点击退出会执行的代码
void CIDEDlg::OnBnClickedOk()
{
// TODO: Add your control notification handler code here
//CDialogEx::OnOK();
}
void CIDEDlg::OnEnChangeEdit1()
{
// TODO: If this is a RICHEDIT control, the control will not
// send this notification unless you override the CDialogEx::OnInitDialog()
// function and call CRichEditCtrl().SetEventMask()
// with the ENM_CHANGE flag ORed into the mask.
// TODO: Add your control notification handler code here
}
void CIDEDlg::On32775()//退出菜单对应的执行函数
{
// TODO: Add your command handler code here
CDialogEx::OnCancel();//把上面点击OK的执行代码修改一下放到这里
}
然后就实现了退出的功能
下面是帮助中的关于,其实系统已经准备好了关于的文件,在资源文件IDE.rc中
是这个样子的
然后就可以对其进行编辑= =
在IDEDlg.cpp文件中这两行代码就是弹出窗口的代码,我们把它复制到关于菜单下(步骤和退出按钮相同)
CAboutDlg dlgAbout;
dlgAbout.DoModal();
下面我们来看看文件菜单下的打开
为其添加事件
在执行代码中,添加
CFileDialog cf(TRUE);//这是一个MFC提供的类,功能是弹出一个文件对话框,TRUE代表打开对话框,FALSE代表保存
if(cf.DoModal()==IDOK);//实现打开,且用户按得是确定
{
CString cs;//MFC提供的字符串类,CString在宽码配置下是一个UCS2编码格式的字符串
//需要把宽码格式的字符串转化为GBK格式的字符串
cs=cf.GetPathName();//返回文件路径+文件名
//MessageBox(cs);//弹出一个对话框,对话框里显示这个字符串
CStringA file_name(cs);//调用CStringA的构造函数,将宽码字符串转化为ASCII字符串,GBK基于ACSII
FILE *p=fopen(file_name.Getbuffer(),"rb");//并不能直接用file_name应为CStringA并不是const char*
CStringA content;//存放文件的内容
if(p)
{
while(ifeof(p))
{
char buf[1024]={0};
fgets(buf,sizeof(buf),p);//读一行
content+=buf;//字符串追加
}
fclose(p);
CString con;
con=content;//吧ASCII字符转化为宽码
edit1.SetWindowText(con);//设置edit1内容
}
}
//如果按的取消,程序什么都不做
保存和打开很像
CFileDialog cf(False);//这是一个MFC提供的类,功能是弹出一个文件对话框,TRUE代表打开对话框,FALSE代表保存
if(cf.DoModal()==IDOK);//实现打开,且用户按得是确定
{
CString cs;//MFC提供的字符串类,CString在款吗配置下是一个UCS2编码格式的字符串
//需要把宽码格式的字符串转化为GBK格式的字符串
cs=cf.GetPathName();//返回文件路径+文件名
CString con;
edit1.GetWindowText(con);//将用户在edit中输入的内容放入变量con
CStringA content(con);//把宽码转化为ASCII字符串
CStringA file_name(cs);
FILE *p=fopen(file_name.Getbuffer(),"w");
fputs(content.GetBuffer(),p);
fclose(p);
}
//如果按的取消,程序什么都不做
下面设置新建,只要加这两句就行了
CString cs;
edit1.SetWindowText(cs);
下面来看复制
edit1.Copy();
同理,粘贴
edit1.Paste();
剪切
edit1.Cut();
以上大概是实现了一个记事本的功能,下面来实现编译的功能
在按钮中添加编译,并添加事件
之前写的file_name 是局部的,我们把它放到.h里面去,它的功能就是得到用户保存的文件名
private:
CStringA filename;
加了这句之后,之前的file_name 就要进行一次赋值操作
filename=file_name;//目的是在其他函数中得到用户保存的文件名
我们使用gcc来编译,在编译按钮的程序中,我们添加
CSstringA destname=filename;
destname.Replace(".c",".exe");//把文件名换成.exe
CStringA cmd="gcc -o "+ destname+filename;//得到命令行中的形式
system(cmd.Getbuffer());
下面是运行
CSstringA destname=filename;
destname.Replace(".c",".exe");//把文件名换成.exe
system(destname.Getbuffer());
浏览器
加入一个文本编辑框,调整其大小,可以在里面输入网址,原来窗口上带的确定和取消分别改成进入网页和退出,并调整其位置
首先需要在窗口上右键,点击插入ActiveX控件如图
为文本编辑框添加变量,名叫text1,且类型为string,代表用户输入的网址
然后给控件添加变量,类别为control,名字叫web
然后和之前的程序一样,把控件的大小调整成和窗口一样,直接复制之前的代码就可以,但是发现它甚至覆盖了按钮= =太大了,就需要把它向下调整:
CRect rect;//MFC提供类,实现了一个矩形结构
GetClientRect(&rect);//得到窗口客户区大小
edit1.SetWindowPos(NULL, 0, 50, rect.Width(), rect.Height()-50, 0);//设置edit1的位置和大小(0,0)指的是相对窗口的坐标 这三行代码实现了控件和实际窗口大小相同,经过调整,再将其向下移动一点
然后调整确定的代码,添加如下代码
UpdateData(True);
web1.Navigate(text1,NULL,NULL,NULL,NULL);
再添加一个按钮实现返回
在这个按钮的代码中添加
web1.GoBack();