C++复习笔记

  1 // CPPTEST.cpp : 定义控制台应用程序的入口点。
  2 //
  3 
  4 #include "stdafx.h"
  5 
  6 #include<map>
  7 #include<vector>
  8 #include<string>
  9 #include<list>
 10 #include<iostream>
 11 #include <functional>
 12 #include <fstream>
 13 #include <regex>
 14 
 15 using namespace std;
 16 
 17 class CBase{
 18 protected://注意,可以使用C#风格的定义时初始化
 19     std::string name = "NoOne";
 20     int age = -20;
 21     int sex = 1;
 22 public:
 23     float *pData = new float[20]{1, 2, 3, 4, 5};
 24 public:
 25     virtual ~CBase(){//虚析构函数,防止内存泄漏:对基类指针调用delete时,会从子类一直析构到基类
 26         cout << "~cbase" << endl;
 27     }
 28 };
 29 
 30 //基类的私有成员不会被继承,这和C#完全一样
 31 class CStudent : public CBase{
 32 public:
 33     std::map<int, std::string> _projs;
 34     CStudent(){
 35         pData = new float[20]{1, 2, 3, 4, 5};
 36     }
 37 public:
 38     void SetName(const std::string& name){
 39         CBase::name = name;//如果CBase.name定义为私有,这里就不可访问
 40         this->name = name; //等价于上一行
 41     }
 42 
 43     const string& GetName(){
 44         return this->name;
 45     }
 46 
 47     ~CStudent(){//若采用浅拷贝,析构函数被调用多次,pData被删除多次,程序崩溃
 48         //规避方式:判断pData是否为空,非空才delete[] pData
 49         //但在不知情的情况下使用pData仍然会出问题,因此浅拷贝导致的问题不可规避
 50         cout << "~cstudent" << endl;
 51         delete[] pData;
 52         pData = NULL;
 53     }
 54 };
 55 
 56 void TestSTL(){
 57     
 58     auto mp = new std::map<int, std::string>();//c++11新风格 auto
 59     
 60     mp->insert({ 10, ("h你好") });//c++11新风格,不用再使用std::pair()或std::make_pair()
 61     mp->insert({ 20, "el" });
 62     for (auto var : *mp)//c++11新风格for
 63     {
 64         std::cout << var.first << "," << var.second << "," << std::endl;
 65     }
 66 }
 67 
 68 void TestClass(){
 69     CBase* pbase = new CStudent();
 70     auto pst = (CStudent*)pbase;
 71     pst->SetName("xxxx");
 72     auto name = pst->GetName();
 73     delete pbase;
 74 }
 75 
 76 int TestAdd(int a, int b){
 77     return a + b;
 78 }
 79 void TestStdFunc(std::function<int(int,int)> fun, int a, int b){
 80     auto ret = fun(a, b);
 81 }
 82 
 83 typedef int(*TestAddPtr)(int, int);
 84 
 85 void TestPCall(TestAddPtr func, int a, int b){
 86     auto ret = func(a, b);
 87 }
 88 
 89 struct Vertex{
 90     bool isgood;
 91     float x, y, z;
 92     double dx;
 93     bool bx;
 94     int ix;
 95     bool by;
 96 };
 97 void TestFile(){
 98     int szChar = sizeof(char);
 99     ofstream ofs;
100     ofs.open("f:/test.txt");
101     ofs << "hello " << 10 << " world " << 20 << endl;
102 
103     ofs.flush();
104     ofs.close();
105     
106     ifstream ifs;
107     ifs.open("f:/test.txt");
108     string str1, str2;
109     int num1, num2;
110 
111     ifs >> str1 >> num1 >> str2 >> num2;
112 
113     //错误示例:二进制读写,使用std::<<或>>进行的还是ASCII码的读写
114      ofstream ofsb;
115     ofsb.open("f:/testb", ios::binary);
116     ofsb << "hellob" << 1022;
117 
118     ofsb.flush();
119     ofsb.close();
120      ifstream ifsb;
121 
122     string sx;
123     int nx;
124     ifsb.open("f:/testb", ios::binary);
125     ifsb >> sx >> nx;
126     ifsb.close();
127 
128     //正确做法
129     sx = "binary";
130     nx = 978;
131     ofsb.open("f:/testbx", ios::binary);
132     ofsb.write(sx.c_str(), sx.length()*sizeof(char)+1);
133     ofsb.write((const char*)&(nx), sizeof(int));
134     ofsb.flush();
135     ofsb.close();
136 
137     char sxr[32];
138     int nxr;
139     ifsb.open("f:///testbx", ios::binary);//注意这里的"///"不管有多少个/都等同于一个
140     ifsb.read(sxr, sx.length()+1);
141     ifsb.read((char*)&nxr, 4);
142 
143     //数据转换的更通用方式
144     Vertex vt;
145     vt.bx = true;
146     vt.isgood = false;
147     vt.x = 12;
148     vt.y = 13;
149     vt.z = 14;
150     vt.dx = 3.9;
151     vt.by = 0;
152 
153     ofstream ofsbx;
154     ofsbx.clear();
155     ofsbx.open("f:/testbyx2", ios::binary);
156     ofsbx.write((const char*)&(vt), sizeof(Vertex));
157     ofsbx.flush();
158     ofsbx.close();
159 
160     ifstream ifsbx;
161     Vertex vrt;
162     ifsbx.clear();
163     ifsbx.open("f:/testbyx2", ios::binary);
164     ifsbx.read((char*)&vrt, sizeof(Vertex));
165 
166     string s1 = "hello";
167     string s2 = "wrold";
168     s1 = s1 + s2;
169     auto s3 = s1.substr(1, 2);
170 }
171 
172 //实现较为高效的字符串分割,限制是分割符只能是一个字符,不能是一个串
173 std::list<string> TestStrSplit(string s, char sep){
174     std::list<string> lst;
175     for (int i = 0, j = 0; i < s.length(); ++i){
176         if (s[i] == sep){
177             lst.push_back(s.substr(j, i - j));
178             j = i + 1;
179         }
180     }
181 
182     //注意临时对象作为返回值了,一般情况下这是错误的用法,栈上的临时对象出了函数域后会被释放
183     //但这里STL容器内部重载了=运算符,作了值拷贝就没问题了
184     return lst;
185 }
186 void TestString(){
187 
188     //g正则表达式实现字符串分割
189     string s1 = "a;b;c;dddd;ef;";
190     string s2 = "a123b2673cdd4444a";
191     std::regex re("(\d+)");
192     std::smatch mtch;
193 
194     //这个做法效率挺低且浪费内存,产生了很多中间字符串
195     while (std::regex_search(s2, mtch, re, std::regex_constants::match_default)){
196         cout << mtch.str() << endl;
197         s2 = mtch.suffix();
198     }
199 
200     //这个函数效率要高多了
201     auto lst = TestStrSplit(s1, ';');
202     
203 }
204 
205 //返回栈上的临时对象测试
206 CStudent TestTempObjRet(){
207     CStudent ost; //临时对象
208     return ost; //调用对象的拷贝构造函数
209 }//出了栈后ost被释放,析构函数调用,同时成员对象被析构CStudent.name="",但内置类型仍保持原值
210 
211 //通过测试可知,将栈上对象作为函数返回值使用一般是没有问题的,但浅COPY时两个对象中的指针指向同一份
212 //内存,当一个对象被删除时,另一个对象中的指针就指向非法位置了,成了野指针
213 void TestObjConstructorAndDestructor(){
214     CStudent ostx;
215     ostx = TestTempObjRet(); //调用拷贝构造函数(与上面对应)
216     auto name = ostx.GetName();
217     auto px = ostx.pData;
218 }
219 
220 void TestRRef(){
221 
222 }
223 
224 //可以使用随机访问(数组下标)说明vector在内存中是连续存放的
225 //这样,vector在需要扩充容量时就需要将原来内存删除,再申请一块新内存
226 //但这并不一定,因为内存申请时若用realloc则有可能会在原内存后面增加(原理)
227 void TestVector(){
228     std::vector<string> sv{ "hello", "world" };
229     sv[0];
230     sv[1];
231     
232     sv.reserve(20); //旧的内容被清除
233     int n = sv.capacity(); //20
234     sv.push_back("a");
235     sv.push_back("b");
236     sv.clear(); //旧的内容被清除
237     n = sv.capacity(); //20
238 
239     sv.shrink_to_fit(); //内存释放
240     n = sv.capacity(); //0
241 
242 }
243 
244 struct CTA{
245 private:
246     virtual void Test(){
247         cout << "cta" << endl;
248     }
249 
250 };
251 
252 class CCTA : CTA{//类和结构体可以相互继承
253 public:
254     int _id;
255     void Test() const{
256         cout << "ccta-test" << endl;
257     }
258 };
259 
260 //C++中字符串有常量和变量之分,字符串遇到\0则结束
261 //C#中只有常量字符串,字符串遇到\0不结束,视其为正常字符
262 void TestStr(){
263     char* ps = "hello";//字符串常量,不可修改其内容
264     ps[0] = 'd'; //运行出错
265 
266     char arr[] = "hello"; //字符串变量
267     char* par = arr;
268     arr[0] = 'd'; //ok
269 }
270 
271 //C++中指针字符串与数组字符串都是自动以0结尾的
272 void TestMemcpy(){
273     
274     char dest[18];
275     char src[] = "hell"; //以0结尾,长度为5,若强制声明为 char src[4] = "hell"则编译报错
276     char* psrc = "hell"; //以0结尾,长度为5,但测试长度strlen(psrc)为4,因为它没算尾0
277     
278     for (int i = 0; i < 10; ++i){
279 
280     }
281     for (int i = 0, ch; (ch = psrc[i++]) != 0;){
282         //这里发现字符串尾0后有许多个0,不知道原因
283 
284     }
285     auto len = strlen(psrc); //4,测试长度,并没字符串的真实长度(内存中真实串),因为它有尾0
286     int len2 = strlen(src); //5,字符串实际长度(内存中存储的字符串)
287     int st = sizeof(src); //5,数组大小
288     memcpy(dest, psrc, strlen(psrc)+1);
289 }
290 template<typename T1, class T2> class MyVector{
291     std::vector<int> _lst;
292 
293 public:
294 
295     void Test2();
296 };
297 
298 template<class T1, class T2> void MyVector<T1, T2>::Test2(){
299 
300 }
301 int _tmain(int argc, _TCHAR* argv[])
302 {
303     TestMemcpy();
304 //     auto pox = new CCTA();
305 //     pox->Test();
306 // 
307 //     MyVector<int, float> _v;
308     //_v.Test<float>();
309     //TestSTL();
310     //TestClass();
311     //TestStdFunc(TestAdd, 10, 20);
312     //TestPCall(TestAdd, 10, 23);
313     //TestFile();
314     //TestString();
315     //TestObjConstructorAndDestructor();
316     //TestVector();
317 
318 
319 
320     return 0;
321 }

 

posted @ 2018-04-04 16:20  时空观察者9号  阅读(192)  评论(0编辑  收藏  举报