指针转化
#include<iostream>
using namespace std;
class node {
public:
char c[1024];
int length;
node() {
length = sizeof(node);
}
};
int main() {
node n;
cin >> n.c;
char* c = (char*)&n; //将n的地址转换成char*,赋值给c
cout << &n << endl;
cout << (int*) c << endl;//打印c指向的地址,可以看出与n的地址一样
node *p = (node*)c; //用一个node* 接收c的值 ,此时p就是指向n的指针
cout << p->c << endl; //打印字符串
return 0;
}
输出结果:
hello
0133F798
0133F798
hello
1.如果给cout提供一个指针,它将打印指针所指向的地址单元的地址,但如果指针类型为char *,则cout将打印char *指针所指向的字符串。
2.如果要显示char *指针所指向的地址单元的地址,需要将char *类型的指针强制转化为另一种类型的指针,我将char *类型的指针强制转化为int *类型指针。
不定参数
int f(int a,...);
这样跟在a
后面的三个点代表不定参数,意味着后面还可以跟多个参数。
后面的参数类型跟a
一样。
暂时觉得没必要去深究底层栈那些。
看看怎么用。
stdarg.h
提供了几个函数:
va_start(list,param1)
,获取可变参数列表的第一个参数的地址。
va_arg(list,mode)
,获取可变参数的当前参数。
va_end(list)
, 清空va_list可变参数列表。
void MyPrintF( const char * format, ... )
{
int size = vsnprintf(NULL, 0, format, arg_ptr);
char* content = new char[size + 1];
va_list args;
va_start (args, format);
vsnprintf(content, size + 1, format, arg_ptr);
va_end (args);
cout<<content<<endl;
}
int main()
{
MyPrintF("my name is %s,my age is %d\n","bob",18);
return 0;
}
这个片段一般用来打印日志。
sstream 类型转换
主要用来进行数据类型转换,相比 C 编程语言库的数据类型转换, 更加安全、自动和直接。
以下代码包含头文件:
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
题目1: 将(int)1000
转化成字符串。
int main()
{
stringstream sstream;
string strResult;
int nValue = 1000;
sstream << nValue;
sstream >> strResult;
cout << strResult << endl;
printf(" %s\n", strResult.c_str());
return 0;
}
题目2:拼接多个字符串,输出,然后重新拼接多个字符串输出。
int main()
{
stringstream sstream;
sstream << "first" << " " << "string,";
sstream << " second string";
cout << sstream.str() << endl;
sstream.str("");
sstream << "third string, ";
sstream << "fourth string";
cout << sstream.str() << endl;
return 0;
}
题目3:将(string)5546.16
转(double)5546.16
类型输出,再将(string)1
转化成bool
类型输出。
int main()
{
stringstream sstream;
sstream << "5546.16";
double w = 0;
sstream >> w;
cout << w << endl;
cout << sstream.str() << endl;
sstream.clear();
//sstream.str("");
sstream << true;
int e;
sstream >> e;
cout << e << endl;
return 0;
}
在本示例涉及的场景下(多次数据类型转换),必须使用 clear() 方法清空 stringstream,不使用 clear() 方法或使用 str(“”) 方法,都不能得到数据类型转换的正确结果。
文件操作
c++提供了三个文件操作流。
ofstream //写文件
ifstream //读文件
fstream //读写文件
函数参数:
- filename 要打开文件的文件名
- mode 打开文件的方式
mode参数 | 方式 |
---|---|
in | 读的方式打开文件/ifstreamd 的默认类型 |
out | 写的方式打开文件/ofstream 的默认类型 |
app | 所有操作都定位到文件末尾 |
trunc | 打开的时候定位到文件末尾 |
ate | 打开的时候定位到文件末尾 |
题目1:将一串字符写入out.txt
文件,然后再取出,再将一串字符写入到out.txt
文件的数据末尾。
int main() {
ofstream out("out.txt");
if (out.is_open())
{
out << "This is a line.\n";
out << "This is another line.\n";
out.close();
}
ifstream in("out.txt");
string buffer;
while (getline(in, buffer))
{
cout << buffer << endl;
}
ofstream out("out.txt",ios::app);
if (out.is_open())
{
out << "This is a line.\n";
out << "This is another line.\n";
out.close();
}
return 0;
}
上面代码向out
流类输入了两个字符串,然后取出。
getline
函数一行一行取出数据,直至取完,然后返回空。
ofstream out("out.txt",ios::app);
将文件打开,定位到末尾,后面再输入数据就可追加到文件末尾。
多态
多态已经是耳熟能详的东西了,网上关于多态的解释教学数不胜数,但是我还是想简单说一下。
首先是c++实现多态必须要有virtual。
c++多态有两种表现形式:
- 基类指针指向子类对象
- 子类对象通过引用赋值给基类
第一种的形式是:
class Vehicle
{
public:
virtual void run()
{
cout << "开交通工具" << endl;
};
};
class ride : public Vehicle
{
public:
void run()
{
cout << "开自行车" << endl;
}
};
class car:public Vehicle
{
public:
void run()
{
cout << "开汽车" << endl;
}
};
int main() {
//1
Vehicle *c = new ride() ;
c->run();
delete(c);
c = NULL;
//2
car s;
Vehicle &o = s;
o.run();
return 0;
}
output:
开自行车
开汽车
值得注意的是,以上代码如果写成:
int main() {
ride s;
Vehicle c = s;
c.run();
return 0;
}
output:
开交通工具
这就不是多态。
这就与虚函数表有关。
顺带一提,通过new 得到的空间要记得释放,防止内存泄漏。释放之后将指针置为空,防止野指针。
虚函数表
class Vehicle
{
public:
void run()
{
cout << "开交通工具" << endl;
};
};
int main() {
cout << sizeof(Vehicle) << endl;
return 0;
}
output:
1
从上面可以看出 class占用的内存空间为1;
class Vehicle
{
public:
virtual void run()
{
cout << "开交通工具" << endl;
};
};
int main() {
cout << sizeof(Vehicle) << endl;
return 0;
}
output:
4
加了virtual后变成了4;
事实上当类中有虚函数时,编译器会生成一个虚函数表virtual table
。同时生成一个指针void bptr
指向表。虚函数表指向类的虚函数,来实现多态,这个虚函数表会一直伴随类;具体虚函数表怎么实现多态这里就不讲了 ,我也不会 。但是我想也没必要纠结这么细,毕竟底层已经做好了,我们也改不了。
虚析构
上面讲了多态的构造要使用虚函数,那么当我们想通过多态,也就是用父类指针销毁子类对象时,要怎么做?
析构函数是在删除对象或退出程序的时候,自动调用的函数,其目的是做一些资源释放。
那么在多态的情景下,通过基类的指针删除派生类对象时,通常情况下只调用基类的析构函数,这就会存在派生类对象的析构函数没有调用到,存在资源泄露的情况。
看这个例子:
class Vehicle
{
public:
int a[10];
Vehicle()
{
cout << "Vehicle构造" << endl;
}
~Vehicle()
{
cout << "Vehicle析构" << endl;
}
};
class ride : public Vehicle
{
public:
ride()
{
cout << "ride构造" << endl;
}
~ride()
{
cout << "ride析构" << endl;
}
};
int main() {
Vehicle * c = new ride();
delete(c);
c = NULL;
return 0;
}
output:
Vehicle构造
ride构造
Vehicle析构
程序删除c的时候,没用调用子类的析构函数。
显然,销毁对象要调用析构函数,那么销毁对象就要将父类的析构设为虚析构。
将上述的代码中的父类的析构函数,定义成「虚析构函数」:
class Vehicle
{
public:
Vehicle()
{
cout << "Vehicle构造" << endl;
}
virtual ~Vehicle()
{
cout << "Vehicle析构" << endl;
}
};
class ride : public Vehicle
{
public:
ride()
{
cout << "ride构造" << endl;
}
~ride()
{
cout << "ride析构" << endl;
}
};
int main() {
Vehicle * c = new ride();
delete(c);
c = NULL;
return 0;
}
output:
Vehicle构造
ride构造
ride析构
Vehicle析构