C++基础入门
1.面向对象
<1>直接定义类
class People { public: void sayHello() { printf("hello c oop \n"); } };
People * p = new People(); p->sayHello();
<2>使用头文件定义
Ml.h
#ifndef ML_H_ #define ML_H_ namespace cjyn { // 命名空间 class Ml { public: void sayHello(); }; } #endif /* ML_H_ */
Ml.cpp
#include "Ml.h" #include <iostream> #include <stdio.h> namespace cjyn { void Ml::sayHello() { // 返回类型 类名::方法名 printf("hello cpp"); } }
main.cpp 调用
#include <iostream> #include "Ml.h" #include <stdio.h> using namespace cjyn; int main(int argc, const char* argv[]) { cjyn::Ml *ml = new cjyn::Ml(); ml->sayHello(); delete ml; // 释放对象,如同C中的free }
补充:如果没有引入using namespace cyjn,则在申明类的时候需要使用(命名空间::类名)的方式,如果引入了命名空间,则上面的申明可以直接使用类名而不需要使用命名空间;
2.类的继承(People->Man)
People.cpp
#include "People.h" #include <iostream> #include <stdio.h> void People::sayHello() { printf("hello cpp"); } int People::getAge() { return this->age; } People::People(int age) { this->age = age; } People::People() { this->age = 12; }
People.h
#ifndef ML_H_ #define ML_H_ class People { private: int age; public: People(); People(int age); void sayHello(); void setAge(); int getAge(); }; #endif /* ML_H_ */
子类Man:Man.h
#ifndef MAN_H_ #define MAN_H_ #include "People.h" class Man: public People { // public 表示从父类继承下来的成员变量是public的 public: }; #endif /* MAN_H_ */
main.cpp
int main(int argc, const char* argv[]) { Man *m = new Man(); m->sayHello(); //hello cpp delete m; }
3.引用父类方法
<1>.构造方法
Man.h
#ifndef MAN_H_ #define MAN_H_ #include "People.h" class Man: public People { // public 表示从父类继承下来的成员变量是public的 public: Man(int age); void sayHello(); }; #endif /* MAN_H_ */
Man.cpp
Man::Man(int age) : People(age) { // 想要执行父类的构造方法只需要在子类构造方法之后加冒号并且调用父类构造方法即可 }
<2>.其他方法调用
Man.cpp
Man::Man(int age) : People(age) { People::sayHello(); // @1 } void Man::sayHello() { printf("man say hello cpp\n"); }
Man.h
class Man: public People { // public 表示从父类继承下来的成员变量是public的 public: Man(int age); void sayHello(); }; #endif /* MAN_H_ */
main.cpp
int main(int argc, const char* argv[]) { Man *m = new Man(34); printf("age %d \n", m->getAge()); m->sayHello(); // 调用man的sayHello m->People::sayHello(); // 调用People的sayHello @2 delete m; }
父类普通方法的调用之需要在方法名前加(类名::方法名),有两种调用方式@1,@2;
4.面向对象特点
<1>.虚函数、纯虚函数、纯虚类
class People { private: int age; public: People(); People(int age); // 虚函数:定义为虚函数之后就可以在运行的时候动态加载相应的方法(相当于java中多态); virtual void sayHello(){}; // 纯虚函数,相当于java中的抽象方法;对应的类就是抽象类 virtual void eat()=0; void setAge(); int getAge(); }; #endif /* ML_H_ */
纯虚类就是类中所有方法都是纯虚函数,相当于java中的接口;
虚函数和纯虚函数分别由子类去重写(可重写可不重写)和实现(子类必须进行实现);
5.操作符重载
class Point { private: int x, y; public: Point(int x, int y) { this->x = x; this->y = y; } void add(Point p) { this->x += p.x; this->y += p.y; } void operator +=(Point p) { add(p); } int getX() { return this->x; } int getY() { return this->y; } };
实例:
int main(int argc, const char* argv[]) { Point p(10, 10); std::cout << p.getX() << "\n"; p += Point(12, 12); std::cout << p.getX() << "\n"; Point *pp = new Point(5, 5); (*pp) += Point(4, 4); std::cout << pp->getX() << "\n"; }
上述代码输出结果为:10,22,9;
<2>.操作符重载之伪函数
伪函数就是把一个类当作一个方法来调用,下面直接看例子
class Point { public: void operator ()() { std::cout << "hello point" << "\n"; } };
使用(这种方式的优点在于比如把类当作一个参数传递)
int main(int argc, const char* argv[]) { Point p; p(); // 输出hello point }
6.函数指针
C++中函数指针和C语言中函数指针其实比较类似,下面是C语言中的两种定义方法
- void (*Pointer)();
- typedef void (*Pointer)();
接着来看C++中函数指针的定义
- @1
-
class Object { public: void (Object::*sayHi)(); };
- @2
-
class Object; typedef void (Object::*SayHi)(); class Object { public: SayHi sayHi; };
通过上面的例子不难看出,都是返回类型+(指针名称定义)+(参数) ,不过在C++中,需要定义函数指针所在的类,在上面即是限定指针为Object的成员或者Object子类的成员;
下面看一个例子:编写一个类继承自Object,并且给sayHi指针赋值;
class Hello : public Object { public: Hello(){ //sayHi = (void(Object::*)())(&Hello::helloSayHi); 将Hello本身的helloSayHi方法赋予sayHi;使用@1方式定义指针 //sayHi = (SayHi)(&Hello::helloSayHi); 将Hello的helloSzyHi方法赋予sayHi,使用@2方式定义指针 //(this->*sayHi)(); // 执行sayHi指针; } void helloSayHi(){ printf("hello say hi\n"); } };
再来看一个例子(延迟执行):
class Object; typedef void (Object::*CallaterHandler)(); // 定义一个指针,该指针所属类为Object类; #define CH(fp) (CallaterHandler)(&fp) // 取fp的地址,强制转换为CallaterHandler; void threadFunc(Object *obj,CallaterHandler handler,int delay){ // _Thrd_sleep(d); // thread sleep (obj->*handler)(); // 执行handler指针 } void callater(Object *obj,CallaterHandler handler,int delay){ std::thread t(threadFunc,obj,handler,delay); t.join(); } class Object {}; class Hello : public Object { public: Hello(){ //callater(this,(CallaterHandler)(&Hello::helloSayHi),3); callater(this,CH(Hello::helloSayHi),3); } void helloSayHi(){ printf("hello say hi\n"); } };
7.引用
先来看一个例子
class PP { public: PP(int x) { this->x = x; } void add(PP p) { this->x += p.getX(); } int getX() { return this->x; } private: int x; };
上面的add方法传入一个PP对象,在执行相加方法时,会对传入的对象进行内存的拷贝,事实上这样的效率不高,为了防止内存的拷贝,可以把add方法更改为
add(PP &p) // &表示取地址
当然,这样的实现完全可以直接通过指针来代替;
8.有元类
在C++中,默认没家修饰符的字段为private,但是有时候需要把特定的字段公开给某一个类,这时候就需要用到有元类;
class A { friend class B; // 通过这样定义,就可以在B类中访问到num private: int num; public: A() { num = 10; } }; class B { public: B() { A a; printf("%d", a.num); } };
9.集合|容器
int main(int argc, const char* argv[]) { std::list<std::string> l; // 需要导入list l.push_back("hello"); l.push_back("spring"); std::list<std::string>::iterator it = l.begin(); for (; it != l.end(); it++) { std::cout << *it << "\n"; } map<string, string> m; // 需要导入map m.insert(pair<string, string>("key1", "value1")); m.insert(pair<string, string>("key2", "value2")); // m["key1"] = "value1+"; // cout << m.at("key1"); return 0; }
上面的代码输出结果为:hello spring value1+
10.字符串
#include <iostream> #include <string> #include <sstream> using namespace std; int main(int argc, const char* argv[]) { string s; s += "hello "; s += "string"; cout << s << "\n"; stringstream ss; ss << "hello"; ss << 23; ss << "spring" << 25; cout << ss.str()<<"\n"; return 0; }
string ss(4,'a'); // aaaa
在c++中,还有另一种占位符形式的格式化字符串的方法,也比较常见;
float _time = 23.0f; char string[15] = {0}; sprintf(string, "%f Test j", _time);
sprintf();
11.文件操作
#include <iostream> #include <string> #include <sstream> #include <fstream> using namespace std; int main(int argc, const char* argv[]) { // ofstream of("data.txt"); // of << "hello cpp \n"; // of.close(); ifstream inf("data.txt"); char c; inf >> c; cout << c << "\n"; // 读取第一个字符 stringbuf sb; inf >> &sb; cout << sb.str() << "\n"; // 读取全部字符 return 0; }
12.内联函数
内联函数和宏的功能性相仿,内联函数在编译的时候,就把所调用的函数直接替换成为函数的具体实现代码,减少了运行的时候函数调用的开销;
内联函数的定义:
inline char* getName(){ return name; };
只需要在函数的定义前边加一个inline即可;需要注意的是,内联函数的实现代码不能太多,否则可能造成代码膨胀,一般适用于少量的代码块;
13.模板函数与模板类
// 模板函数 template <class T> T getMax(T a,T b){ return a>b?a:b; } // 模板类 template <class T> class LinkList { struct node { T *t; struct node *pNex; }; node * head; public: void addNewNode(T *t); void deleteNode(int id); };
上面分别定义了一个模板函数和一个模板类,使用模板函数的时候,我们可以给getMax传递各种类型的参数,比如int float,而不用分别写一个int和float的比较方法;另一个模板类,则模仿了链表的管理实现;