C++自己做个笔记(来源:菜鸟教程)

构造函数
析构函数

析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
(C++ 初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序)
所以初始化顺序最好要按照变量在类声明的顺序一致。


#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();   // 这是构造函数声明,默认
      Line(double len);  // 这是构造函数,带参数
      ~Line();  // 这是析构函数声明
 
   private:
      double length;
};
 
// 成员函数定义,包括构造函数
Line::Line(void)
Line::Line( double len)//带参数的构造函数初始化
{
    cout << "Object is being created" << endl;
}
Line::~Line(void)
{
    cout << "Object is being deleted" << endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
   Line line;
 
   // 设置长度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}

指针*(指向地址),引用&(取地址,引用更接近const指针,必须在创建时进行初始化),
简单点就是:&:取址。* ,取值,数组名是指针,
指针的本质是变量,可以是各种数据类型,定义一个指针 “*ip”,其中 “ip” 需要赋于一个地址(可以用 & 符号获取其他变量的地址再赋值给 ip),而 “*ip” 是一个具体的值,即读取地址后获得的值;
实例代码:

#include <iostream>
using namespace std;

int main()
{
    int var = 20;
    int *ip;
    ip = &var;

    cout << "var的值:";
    cout << var << endl;

    cout << "变量 ip 的储存地址:";
    cout << ip << endl;

    cout << "指针 *ip 的值:";
    cout << *ip << endl; 
    return 0;
}
/*var的值:20
变量 ip 的储存地址:0x7fff5e7deae8
指针 *ip 的值:20*/

多态

形成多态必须具备三个条件:
1、必须存在继承关系;
2、继承关系必须有同名虚函数(其中虚函数是在基类中使用关键字Virtual声明的函数,在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数);
3、存在基类类型的指针或者引用,通过该指针或引用调用虚函数;


class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      virtual int area()
      {
         cout << "Parent class area :" <<endl;
         return 0;
      }
};
class Rectangle: public Shape{
   public:
      Rectangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Rectangle class area :" <<endl;
         return (width * height); 
      }
};
class Triangle: public Shape{
   public:
      Triangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Triangle class area :" <<endl;
         return (width * height / 2); 
      }
};
// 程序的主函数
int main( )
{
   Shape *shape;
   Rectangle rec(10,7);
   Triangle  tri(10,5);
 
   // 存储矩形的地址
   shape = &rec;
   // 调用矩形的求面积函数 area
   shape->area();
 
   // 存储三角形的地址
   shape = &tri;
   // 调用三角形的求面积函数 area
   shape->area();
   
   return 0;
}

结构体( 数据结构)


struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book;//定义一个结构体类型 Books,声明一个变量为 book

类(类中同名函数为构造函数)、继承、虚函数()、纯虚函数( virtual int area() = 0;)

访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。

// 基类
class Animal {
    // eat() 函数
    // sleep() 函数
};


//派生类
class Dog : public Animal {
    // bark() 函数
};

重载运算符和重载函数【在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。】


#include <iostream>
using namespace std;
 
class printData
{
   public:
      void print(int i) {
        cout << "整数为: " << i << endl;
      }
 
      void print(double  f) {
        cout << "浮点数为: " << f << endl;
      }
 
      void print(char c[]) {
        cout << "字符串为: " << c << endl;
      }
};
 
int main(void)
{
   printData pd;
 
   // 输出整数
   pd.print(5);
   // 输出浮点数
   pd.print(500.263);
   // 输出字符串
   char c[] = "Hello C++";
   pd.print(c);
 
   return 0;
}

模板typename

// 函数模板
template <typename type> ret-type func-name(parameter list)
{
   // 函数的主体
}
// 类模板
template <class type> class class-name {
.
.
.
}


#include <iostream>
using namespace std;
 
class Box
{
   public:
 
      double getVolume(void)
      {
         return length * breadth * height;
      }
      void setLength( double len )
      {
          length = len;
      }
 
      void setBreadth( double bre )
      {
          breadth = bre;
      }
 
      void setHeight( double hei )
      {
          height = hei;
      }
      // 重载 + 运算符,用于把两个 Box 对象相加
      Box operator+(const Box& b)
      {
         Box box;
         box.length = this->length + b.length;
         box.breadth = this->breadth + b.breadth;
         box.height = this->height + b.height;
         return box;
      }
   private:
      double length;      // 长度
      double breadth;     // 宽度
      double height;      // 高度
};
// 程序的主函数
int main( )
{
   Box Box1;                // 声明 Box1,类型为 Box
   Box Box2;                // 声明 Box2,类型为 Box
   Box Box3;                // 声明 Box3,类型为 Box
   double volume = 0.0;     // 把体积存储在该变量中
 
   // Box1 详述
   Box1.setLength(6.0); 
   Box1.setBreadth(7.0); 
   Box1.setHeight(5.0);
 
   // Box2 详述
   Box2.setLength(12.0); 
   Box2.setBreadth(13.0); 
   Box2.setHeight(10.0);
 
   // Box1 的体积
   volume = Box1.getVolume();
   cout << "Volume of Box1 : " << volume <<endl;
 
   // Box2 的体积
   volume = Box2.getVolume();
   cout << "Volume of Box2 : " << volume <<endl;
 
   // 把两个对象相加,得到 Box3
   Box3 = Box1 + Box2;
 
   // Box3 的体积
   volume = Box3.getVolume();
   cout << "Volume of Box3 : " << volume <<endl;
 
   return 0;
}

命令空间(作用域A::B,当前作用域::a)

namespace namespace_name {
   // 代码声明
}
using namespace namespace_name;
namespace_name::方法()
namespace_name::变量
  • 全局变量和和局部变量同名时,可通过域名在函数中引用到全局变量,不加域名解析则引用局部变量
#include<iostream>using namespace std;
int a = 10;int main(){
    int a = 20;
    cout << ::a << endl;   // 10
    cout << a << endl;     // 20
    return 0;
    }
  • 定义成 const 后的常量,程序对其中只能读不能修改。

以下程序是错误的,因为开头就已经固定了常量,便不能再对其进行赋值:

#include <iostream>
using namespace std;
int main()
{
    const double pi;                      //圆周率的值用pi表示
    pi=3.14159265;
    cout<<"圆周率的近似值是"<<pi<<endl;
    return 0;
}

下面给出正确的赋值方法:

#include <iostream>
using namespace std;
int main()
{
    const double pi=3.141592;            //圆周率的值用pi表示
    cout<<"圆周率的近似值是"<<pi<<endl;
    return 0;
}
  • 预处理 #define 变量定义值以后,不能用分号,否则就会计算错误,但是程序不会报错。
#define age  12
#define age1 10
#define age2  12;
#define age3 10;
int main()
{
   int dd  ; 
   dd = age + age1;
   cout  << "值=" << dd << endl; //值22
   dd = age2 + age3;
   cout  << "值=" << dd << endl; //值12
    return 0;  
}

指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。
指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。

  • 运算符优先级助记歌(自己编的,优先级从高到低)
先算右一后左一,①
乘除加减移位比。②
与异或或位逻辑,③
三目赋值逗号稀。④

①先算右面的一元运算符,后算左面的。
②乘除代表*/%,加减就是+和-,移位就是<<和>>,比就是比较运算符,注意比较运算符先算<、>、<=和>=这4个含不等号的,后算==和!=这两个。
③先算按位逻辑运算符,再算普通的逻辑运算符;按位逻辑运算符的顺序是&^|,逻辑运算符先算&&再算||,只是少了逻辑异或。
④先算三目运算符,再算赋值运算符,逗号运算符的优先级最低,所以说它“稀”。

  • 两个数互换,使用异或
int swap(int& a, int& b)
{
    int temp;
    temp = a ^ b;
    a = temp ^ a;
    b = temp ^ b;
    return 0;
}

Lambda 函数与表达式

[]:默认不捕获任何变量;
[=]:默认以值捕获所有变量;
[&]:默认以引用捕获所有变量;
[x]:仅以值捕获x,其它变量不捕获;
[&x]:仅以引用捕获x,其它变量不捕获;
[=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获;
[&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;
[this]:通过引用捕获当前对象(其实是复制指针);
[*this]:通过传值方式捕获当前对象;
  • Array 直接初始化 char 数组是特殊的,这种初始化要记得字符是以一个 null 结尾的。

a4 是错误的,虽然 a4 包括 6 个直接字符,但是 array 大小是 7:6个字符 + 一个null。

char a1[] = {'C', '+', '+'};          // 初始化,没有 null
char a2[] = {'C', '+', '+', '\0'};    // 初始化,明确有 null
char a3[] = "C++";                    // null 终止符自动添加
const char a4[6] = "runoob";          // 报错,没有 null 的位置
正确的是:
const char a4[7] = "runoob";
  • Array 是固定大小的,不能额外增加元素.当我们想定义不固定大小的字符时,可以使用 vector(向量) 标准库。

指针和引用

  • C++ 中使用指针

使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:

#include <iostream>
using namespace std;

int main ()
{
   int  var = 20;   // 实际变量的声明
   int  *ip;        // 指针变量的声明
 
   ip = &var;       // 在指针变量中存储 var 的地址
 
   cout << "Value of var variable: ";
   cout << var << endl;
 
   // 输出在指针变量中存储的地址
   cout << "Address stored in ip variable: ";
   cout << ip << endl;
 
   // 访问指针中地址的值
   cout << "Value of *ip variable: ";
   cout << *ip << endl;
 
   return 0;
}
  • 指针指向数组的时候,不可以加 &:
    指针指向数组中某一元素时要用 &
int main()
{
    int  var[5] = {1,2,3,4,5};   // 实际变量的声明
    int  *ip;        
    // 指针变量的声明  
    int  *ip_1;
    //指针指向数组的时候不用 “&”取址符
    // 在指针变量中存储 var 的地址
    ip = var; 
    //指针指向数组某一元素时要用 “&”取址符
    // 在指针变量中存储 var[2] 的地址
    ip_1 = &var[2];
    
    return 0
}
  • 引用,看做别名

引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
试想变量名称是变量附属在内存位置中的标签,您可以把引用当成是变量附属在内存位置中的第二个标签。
把引用作为参数,C++ 支持把引用作为参数传给函数,这比传一般的参数更安全。
把引用作为返回值,可以从 C++ 函数中返回引用,就像返回其他数据类型一样。

int& r = i; 和 int r = i; 不同之处应该是内存的分配吧,后者会再开辟一个内存空间

  • 数组的引用一定要表明数组的大小。
 int a[] = { 1,2,3,4 };
    int(&t)[4] = a;
  • 引用int& var=var1 和 指针int *var的区别,取地址 int &var
    引用很容易与指针混淆,它们之间有三个主要的不同:

不存在空引用。引用必须连接到一块合法的内存。
一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
引用必须在创建时被初始化。指针可以在任何时间被初始化。

  • C++11 新增特性 – 右值引用

加入右值引用的目的主要是为了解决这样一个问题:在函数传值以及返回值的时候,一个对象被复制给另外一个对象,然后这个对象紧接着就被析构了。显然很浪费时间。使用右值引用的话就可以把一个对象的所有权“转让”给另外一个对象,而无需调用复制构造函数和析构函数。写法示例:

int &&rvalueReference = 1 + 2;

类与结构体

结构体(C与C++结构体中前者不能有函数,后者可以有。)

type_name 是结构体类型的名称,member_type1 member_name1 是标准的变量定义,比如 int i; 或者 float f; 或者其他有效的变量定义。在结构定义的末尾,最后一个分号之前,您可以指定一个或多个结构变量,这是可选的。下面是声明一个结构体类型 Books,变量为 book

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book(初始化了一个结构体,C++里面叫声明);
  • 使用new动态创建结构体变量

使用new动态创建结构体变量时,必须是结构体指针类型。访问时,普通结构体变量使用使用成员变量访问符".“,指针类型的结构体变量使用的成员变量访问符为”->"。注意:动态创建结构体变量使用后勿忘delete。

  • C++ 中的 struct 对 C 中的 struct
    进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。

struct 能包含成员函数吗? 能!
struct 能继承吗? 能!!
struct 能实现多态吗? 能!!!

  • C++中的struct和class基本是通用的,有几个不同之处:

使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。
class 继承默认是 private 继承,而 struct 继承默认是 public 继承(《C++继承与派生》一章会讲解继承)。
class 可以使用模板,而 struct 不能(《模板、字符串和异常》一章会讲解模板)。

  • 一个派生类继承了所有的基类方法,但下列情况除外:

    基类的构造函数、析构函数和拷贝构造函数。
    基类的重载运算符。
    基类的友元函数。

  • 继承类型

当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。
我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:
公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
私有继承(private): 当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

1、与类同名的函数是构造函数。
2、~ 类名的是类的析构函数。

  • 运算重载符注意事项:

1、运算重载符不可以改变语法结构。
2、运算重载符不可以改变操作数的个数。
3、运算重载符不可以改变优先级。
4、运算重载符不可以改变结合性。

  • 类重载、覆盖、重定义之间的区别:

重载 指的是函数具有的不同的参数列表,而函数名相同的函数。重载要求参数列表必须不同,比如参数的类型不同、参数的个数不同、参数的顺序不同。如果仅仅是函数的返回值不同是没办法重载的,因为重载要求参数列表必须不同。(发生在同一个类里)
覆盖 是存在类中,子类重写从基类继承过来的函数。被重写的函数不能是static的。必须是virtual的。但是函数名、返回值、参数列表都必须和基类相同(发生在基类和子类)
重定义也叫做隐藏重定义也叫做隐藏,子类重新定义父类中有相同名称的非虚函数 ( 参数列表可以不同 ) 。(发生在基类和子类)

  • 虚函数(virtual修饰符则强调父类的成员函数可以在子类中被重写)

父类的虚函数或纯虚函数在子类中依然是虚函数。有时我们并不希望父类的某个函数在子类中被重写,在 C++11 及以后可以用关键字 final 来避免该函数再次被重写。

  • 类中的构造函数可以初始化类变量

#include <iostream>
using namespace std;
 
class Adder{
   public:
      // 构造函数
      Adder(int i = 0)
      {
        total = i;
      }
      // 对外的接口
      void addNum(int number)
      {
          total += number;
      }
      // 对外的接口
      int getTotal()
      {
          return total;
      };
   private:
      // 对外隐藏的数据
      int total;
};
int main( )
{
   Adder a;
   
   a.addNum(10);
   a.addNum(20);
   a.addNum(30);
 
   cout << "Total " << a.getTotal() <<endl;
   return 0;
}

全局变量 a 表达为 ::a,用于当有同名的局部变量时来区别两者。


#include <iostream>
using namespace std;
 
// 第一个命名空间
namespace first_space{
   void func(){
      cout << "Inside first_space" << endl;
   }
}
// 第二个命名空间
namespace second_space{
   void func(){
      cout << "Inside second_space" << endl;
   }
}
int main ()
{
 
   // 调用第一个命名空间中的函数
   first_space::func();
   
   // 调用第二个命名空间中的函数
   second_space::func(); 
 
   return 0;
}

自定义命名空间

以下代码会出错:会显示 a 变量和 fun 函数 “was not declared in this scope”,即找不到这个 a 和 fun 函数。
解决办法: 用 using 来告诉编译器用到的是哪个命名空间内的内容。在 main() 上面加 using namespace A; 或者 using namespace A::B; 。这样就可以使用其中的 a 和 fun()。但是不能同时使用,因为这样也会导致编译出错,编译器器不知道要去使用哪个 a 和 fun()。

#include <iostream>
using namespace std;
namespace A
{
    int a = 100;
    int fun()
    {
        cout<<"a = "<<a<<endl;
    }

    namespace B            //嵌套一个命名空间B
    {
        int a =20;
        int fun()
        {
             cout<<"a = "<<a<<endl;
        }

    }
}


int main(int argc, char *argv[])
{
    cout<<a<<endl;
    fun();

    return 0;
}

函数模板可以重载,只要它们的形参表不同即可。例如,下面两个模板可以同时存在:/font>

template<class T1, class T2>
void print(T1 arg1, T2 arg2)
{
  cout<<arg1<<" "<<arg2<<endl; 
}
template<class T>
void print(T arg1, T arg2)
{
  cout<< arg1<< " "<< arg2<< endl;
}

其实举个简单的例子反而更利于新手理解本质。
函数模板:

#include <iostream>
using namespace std;
template <typename T1>
void Swap(T1& a, T1& b)
{
    T1 t = a;
    a = b;
    b = t;
}
int main()
{
    int a = 2;
    int b = 3;
    cout <<"a = " << a << ";  b = " << b <<endl;
    Swap(a,b);
    cout <<"a = " << a << ";  b = " << b <<endl;
    float c = 0.02;
    float d = 0.03;
    cout <<"c = " << c << ";  d = " << d <<endl;
    Swap(c,d);
    cout <<"c = " << c << ";  d = " << d <<endl;
    return 0;
}

类模板:

#include <iostream>
using namespace std;
template <typename T>
class Op{
public:
    T peocess(T v)
    {
        return v * v;
    }
};
int main()
{
    Op<int> opInt;
    Op<double> opDouble;
    cout << "5 * 5 = " << opInt.peocess(5) <<endl;
    cout << "0.5 * 0.5 = " << opDouble.peocess(0.5) <<endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值