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 }