标签(空格分隔): c语言
C++中的helli-world
#include <iostream> int main(){ std::cout << ”HELLO WORLD“ << std::endl; }预处理器编译指令#include: 预处理器是编译前运行的工具,#include<filename>让处理器获取指定文件的内容,并将这些代码放在编译指令所处的位置返回值: 很多情况下,一个应用程序被另一个程序启动,而父应用程序想知道子应用程序是否成功完成了任务,程序员可通过使用main的返回值向父应用程序传递成功或错误状态名称空间: (1)代码中使用std::out而不是cout,原因在于cout位于标准名称空间中(std)。假设调用cout时没有使用空间限定符,且编译器知道cout位于两个地方,那编译器应该调用哪个呢,这显然会让编译器混乱。 (2)代码中频繁添加std限定符很繁琐,为避免添加限定符,可以使用声明using namespace
using namespace std; cout << "hello world" << endl(3)using不只可以用于限定命名空间这么大的范围,还可以限定到使用的元素c using std::cout; using std::endl; cout << "hello world" << endl;
typedef关键字 (1)C++允许将变量类型替换为您认为方便的名称 (2)格式为: typedef origionType desType
typedef unsigned int STRICTLY_OSITIVE_INTEGER STRICTLY_OSITIVE_INTEGER postNumber = 4532C风格字符串 (1)C风格的字符串用字符数组来声明,而数组都是需要提前计算好元素个数。 (2)字符数组的元素个事应该为字符串中字符个数+1,因为编译器会在双引号引起的内容后面加上\0
char HELLOWORLD2[] = {'h','e','l','l','o','w','o','r','l','d','\0'};C++风格字符串 (1)C风格字符串带来的威胁:C语言程序中的strcpy,strcat,strlen等函数,都会寻找终止空字符,如果程序员没有在字符数组末尾添加空字符,这些函数将产生字符数组越界。 (2)C++提供std::string,这是一种强大而安全的字符串操作方式,他能动态扩展存储大小。
#include <iostream> int main(){ std::string HELLOWORLD2 = "hello world"; std::cout << HELLOWORLD2 << std::endl; }C++程序可以在字节和比特级调整应用程序的性能,要编写高质量的程序利用系统资源,理解指针和引用就必不可少。
声明指针 (1)指针通常声明为指向特定类型,如int,表示指针所示的内存单元存储了一个整数 (2)也可让指针声明为指向一个内存块,这种指针被称为void指针 (3)只声明指针,而不去初始化指针,是给指针赋予了一个垃圾值。因为指针包含的值被视为地址,所以未初始化的指针会导致程序访问非法内存,进而导致程序崩溃
int *pInteger = NULL;&符号获取变量地址 所有变量都在内存的一个地址上,可以通过&varialbe来获取变量所处地址。
#include <iostream> int main(){ int Age = 30; int* addr = &Age; std::cout << "Integer is at: 0x" << std::hex << &Age << std::endl; // Integer is at: 0x0x7fff1422250c std::cout << "Addr is: 0x" << std::hex << addr << std::endl; // Addr is: 0x0x7fff1422250c }使用解除引用运算符*访问指针指向的数据
int Age = 30; int* addr = &Age; cout << dec << *addr << endl;new动态分配内存,delete动态释放内存 (1)使用new来分配新的内存块,需要指定为哪种数据类型分配内存 (2)语法:TypeName* p = new TypeName,delete p (3)分配连续内存:TypeName* p = new TypeName[length],delete[] p ```c using namespace std; int main(){ cout << "Enter your name:"; string name; cin >> name;
int charAllocate = name.length() + 1; // 字符串结尾的\0 char* copyName = new char[charAllocate]; strcpy(copyName,name.c_str()); // 将字符串转换为c类型字符串再拷贝给字符数组 cout << "Alloacated buffer contains:" << copyName << endl; delete[] copyName;} /** Enter your name:lj Alloacated buffer contains:lj */ ```
const关键字用于指针的三种形式 (1)const TypeName* p:当前指针变量指向的内存地址上的数据为常量,不可修改。但指针指向的地址可以改变。c int Age=30; const int* p = &Age; //*p = 40; // 编译报错,这样已经改变了指针所指向内存的数据 int num2 = 35; p = &num2; // 改变指针指向的地址可以,编译通过 cout << dec << *p; (2)TypeName* const p:指针指向的地址为常量,不可修改,但可修改指向地址上的数据值c int Age=30; int* const p = &Age; *p = 40; // 改变地址上的值,编译通过 //int num2 = 35; //p = &num2; // 改变指针指向的地址,编译不通过 cout << dec << *p; (3)const TypeName* const p:指针包含的地址,和改地址指向的值都不能修改c int Age=30; const int* const p = &Age; // *p = 40; // 不能改变地址上的值 //int num2 = 35; //p = &num2; // 不能改变指针指向的地址 cout << dec << *p;
当指针变量拷贝到另一个指针变量时,只需对其中一个指针进行delete,多次delete会导致程序崩溃c int* p = new int; *p = 30; int* p1 = p; delete p; delete p1; // 程序崩溃
检查new发出的请求分配是否得到满足 (1)默认情况下,C++在请求分配内存失败的情况下,抛出std::bad_alloc异常c try{ int* p = new int[9999999999]; delete[] p; } catch(bad_alloc){ cout << "Bad allocation" << endl; } (2)使用new(nothrow)在内存分配失败的情况下返回NULLc int* p = new(nothrow) int[9999999999]; if(p) delete[] p; else cout << "allocate fail"<<endl;
引用是什么 (1)引用是一个变量的别名,应用初始化时,应让它指向一个变量 (2)语法格式TypeName original = valueTypeName& ref = original ```c int original = 30; cout << hex << &original<<endl; // 0x7ffd02a9593c
int& ref = original; //引用变量的值是内存上的数据值 cout << hex << &ref << endl; // 0x7ffd02a9593c,引用和original变量的地址相同 ```引用的用处 (1)因为C++函数的参数传递是值拷贝,当参数所占内存很大时,值复制会消耗大量时间。可以把函数的形参设为引用变量,这样在值传递时,引用指向实参地址,不会发生内存拷贝。 ```c using namespace std;
void square(int& number){ number = number * number; }
int main(){ int number = 10; cin >> number; // 输入5
square(number); cout << number <<endl; // 25 } ```const用于引用 (1)const TypeName& ref = origion:禁止通过引用修改原先变量的值 (2)把函数形参设置为const引用,既可以避免内存拷贝,又可以避免实参得治在函数体中被修改 ```c int square(const int& number){ // number = number * number; 编译错误,禁止通过引用修改变量值 return number*number; }
int main(){ int number = 10; cin >> number;
int result = square(number); cout << result <<endl;}
```
构造函数与setter,getter (1)构造方法写在public代码块内 (2)Person::name:表示在Person类中生命的name属性。::符号又被称为作用域解析运算符 ```c using namespace std;
class Person{ private: string name; int age; public: Person(int age,string name){ // 写在public内的构造函数 this->name = name; this->age = age; }
const string &getName() const { // setter与getter return name; } void setName(const string &name) { Person::name = name; } int getAge() const { return age; } void setAge(int age) { Person::age = age; } void introduceInfo(){ // 表示在Person类中生命的name属性。::符号又被称为作用域解析运算符 cout << "My name is:"<< Person::name << " , age is:"<< Person::age << endl; } }; int main(){ Person* p = new Person(23, "zhangsan"); p->introduceInfo(); } ```构造函数的简写形式 (1)使用Person(int gae,string name):name(name),age(age){}的空函数体形式 ```c class Person{ private: string name; int age; public: Person(int gae,string name):name(name),age(age){} // 构造函数简写形式
void introduceInfo(){ cout << "My name is:"<<Person::name<<" , age is:"<<Person::age << endl; } }; int main(){ Person* p = new Person(23, "zhangsan"); // new返回开辟的内存空间地址起始 p->introduceInfo(); } ```析构函数 (1)析构函数调用时机:当对象不再在作用域内或被delete删除时,对象被销毁,进而调用析构函数 (2)析构函数不能重载,每个类只有一个析构函数,如果没有自定义析构函数,编译器会自动加上一个空的析构函数,这样的话,类中动态申请的内存将无法被释放 ```c class MyString{ private: char* buffer; public: MyString(const char* input){ if (input!= NULL){ this->buffer = new char[strlen(input)+1]; strcpy(buffer,input); // 拷贝input到buffer }else{ buffer = NULL; } }
~MyString(){ cout<< "mystring clearn up!" <<endl; delete[] this->buffer; }};
int main(){ MyString* ms = new MyString("helloworld"); delete ms; // 对象被销毁,自动调用析构函数 } ```
对象浅复制 (1)当一个对象作为函数参数进行传递时,C++的值拷贝只是对象的浅拷贝:只复制其指针成员,不复制指针指向的缓冲区。使得在方法退出时,对象被delete同时也把包含的指针成员指向的缓冲区delete了。当手动delete原函数时,原先的缓冲区已经被delete。会出现错误。 (2)这种错误一般在ms visual stdio模式下会报错
复制构造函数 (1)当程序员手动增加了复制构造函数后,编译器在函数调用时,会使用复制构造函数来创建对象,进行对象值传递。 (2)形式:c class ClassName{ public: ClassName(const ClassName& origion){ ... } }
移动构造函数 (1)因为C++严格按照复制构造函数进行参数传递,在对象的指针成员指向的缓冲区很大时,深复制会造成效率降低 (2)所以在提供复制构造函数的同时,需要提供移动构造函数。形式如下:c ClassName(ClassName&& src)构造函数举例 ```c class MyString{ private: char* buffer; public: // 构造函数 MyString(const char* input){ if (input!= NULL){ this->buffer = new char[strlen(input)+1]; strcpy(buffer,input); // 拷贝input到buffer }else{ buffer = NULL; } }
// 复制构造函数 MyString(const MyString& origion){ cout << "copy constructor runing.." << endl; if(origion.buffer != NULL){ buffer = new char[strlen(origion.buffer) + 1]; strcpy(buffer,origion.buffer); cout << "buffer points to:0x" << hex << (unsigned int*)buffer << endl; } else buffer = NULL; } // 移动构造函数 MyString(MyString&& origion){ cout << "move constructor running" << endl; if(origion.buffer != NULL){ buffer = origion.buffer; // 移动后的对象指向原先的缓冲区 origion.buffer = NULL; // 对象已移动,原先指针废弃 } } char* getbuffer(){ return buffer; }};
void useMyString(MyString input){ //此时参数传递自调用复制构造函数进行对象生成 cout << "input is" << endl; }
int main(){ MyString ms("hello world"); useMyString(ms); } /** copy constructor runing.. buffer points to:0x0xe78050 input is */ ```
单例类 ```c using namespace std;
class President{ private: President(){}; // 私有化所有造方法 President(const President&); // 私有化复制构造方法 const President& operator = (const President&); string name;
public: static President& instant(){ // 暴露一个static的返回引用的方法 static President onlyInstance; // 初始化一个静态对象 return onlyInstance; } string getName(){ return name; } void setName(string inputName){ name = inputName; } };
int main(){ President& onlyInstance = President::instant(); onlyInstance.setName("lj"); cout << "President's name:" << onlyInstance.getName() << endl; // President's name:lj cout << "President's name:" << President::instant().getName() << endl; // President's name:lj } ```不允许在栈中创建的类 (1)方法中没有用new产生的类就会在栈里面,要想不让栈中产生类,需要私有化析构函数。 (2)私有化析构函数后,在函数退出时,不能调用析构方法来销毁对象,所以这样的代码就不能通过编译。 ```c class MonsterDB{ private: ~MonsterDB(); // 私有化析构函数 };
int main(){ MonsterDB myDB; // 编译报错,方法中不能再使用类名创建对象 return 0; } ```
this指针 表示当前对象的内存地址
C++中结构体与类 (1)关键字struct来自于C语言,在C++编译器看来,它与类相似,差别仅在于struct中的属性默认全部是public的,除非显式指定,否则struct以公有类方式继承。 (2)代码示例 ```c struct Human{ // 构造函数 Human(const string& inputname,int inputage):name(inputname),age(inputage){}; private: int age; string name; };
int main(){ Human fistMan("lj",26); } ```
宏函数定义 (1)宏函数通常用于非常简单的计算 (2)宏不考虑数据类型,因此使用宏函数编程很危险
using namespace std; #define MAX(a,b) (a>b?a:b) int main(){ cout << MAX(3,8) << endl; // 8 }使用assert宏函数验证表达式 (1)引入assert函数先导入assert.h文件c #include <assert.h> int main(){ char* sayHello = new char[25]; // sayHello不是空,报错:int main(): Assertion `sayHello == NULL' failed. assert(sayHello == NULL); delete[] sayHello; }
宏函数的优缺点: (1)优点:宏函数将在编译前就地展开,因此简单宏的性能优于函数调用。因为它避免了创建函数栈,传递参数等高开销cpu (2)缺点:宏函数不支持任何形式的类型检查。
模板声明 (1)模板让程序员可以定义一种适用于不同类型对象的行为 (2)模板方法声明:
template <tyoename T1,typename T2> // template参数声明 bool fun1(const T1& p1,const T2& p2);(3)模板类声明:c template <typename T1,typename T2> // template参数声明 class Test{ private: T1 obj1; T2 obj2; public: T1 getObj1(){ return obj1; } };
模板举例 (1)模板函数c using namespace std; // 模板函数 template <typename T> const T& getMax(const T& v1,const T& v2){ if (v1 > v2) return v1; else return v2; } int main(){ int v1 = 25; int v2 = 35; int max = getMax(v1,v2); // 模板函数调用编译器可自行推断参数类型 int max2 = getMax<int>(v1,v2); // 模板函数调用也可自行指定参数类型 cout << max2 << endl; } (2)模板类 ```c template class Person{ private: T1 name; T2 age; public: Person(const T1& name,const T2& age){ this->name = name; this->age = age; } const T1& getName() const{ return name; } const T2& getAge() const{ return age; } };
int main(){ Person <char*,int> p ("zhangsan",23); // 具体化模板类可以手动指定类型 cout << p.getName() << endl; cout << p.getAge() << endl;
Person <> p1 ("lj",26); // 由于模板参数中给出了默认类型,所以p1的具体化可以使用空类型 cout << p1.getName() << endl; cout << p1.getAge() << endl;```
转载于:https://www.cnblogs.com/moonlord/p/6991442.html
相关资源:21天学通C++(第8版)源代码