C++学习笔记

221 阅读7分钟

一、基础知识

  1. main函数被启动代码调用,而启动代码是由编译器添加到程序中的,是程序和操作系统之间的桥梁。
  2. 宏:在C++中不推荐使用宏,因为在后续的排错系统中将带来隐患。尽量以const,enum,inline替换#define
  3. 命名空间:命名空间是一种描述逻辑分组的机制。也就是说,如果一些声明按照某种准则在逻辑上属于同一个集团,就可以将它们放入同一个命名空间,以表明这个事实。
  4. 在Windows编程中,可以编写一个动态链接库(DLL)模块,这是其他Windows程序可以使用的代码。由于DLL模块不是独立的程序,因此不需要main()。
  5. 当使用<iostream>的时候,该头文件没有定义全局命名空间,命名空间std封装的是标准程序库的名称,using namespace std的作用是释放std命名空间中的变量名,函数名以及类型名
  6. 作用域分解运算符"::"
//AB表示两个类,而在A,B中都有成员member
A :: member表示类A成员中的member
B :: member表示类B成员中的member
  1. cout的对象属性包括一个插入操作符<<,它可以将其右侧的信息插入到流中;插入操作符和按位左移操作符都是同一个,这是一个操作符重载的例子,通过重载,同一个操作符将有不同的含义。
  2. 控制符endl:重起一行
  3. 类是用户定义的一种数据类型。要定义类,需要描述它能够表示什么信息和可对数据执行哪些操作。类之于对象就像类型之于变量。也就是说,类定义描述的是数据格式及其用法,而对象则是根据数据格式规范创建的实体。
  4. 使用另一个命名空间的名字
namespace Parser {
    double prim(bool);
    using namesapce Lexer; //使用Lexer的所有名字
    using namepsace Error;
}
  1. 如果两个头文件中拥有相同的函数名,可以通过将每组声明包裹在它自己的命名空间中:
//my.h
char f(char);

//your.h
char f(char);

-----------------

namespace My {
    char f(char);
}

namespace Your {
    char f(char);
}
  1. 标准代码格式
#include<iostream>
using namespace std;

void func(); //输出HelloWorld

int main()
{
    func();
    return 0;
}

void func()
{
    cout << "HelloWorld" << endl;
}
  1. 变量名命名推荐:

前缀n:表示整数值,例如nMyWeight
前缀b:表示布尔值
前缀p:表示指针
前缀c:表示单个字符
前缀s:表示字符串

  1. vscode输出中文:SetConsoleOutputCP(CP_UTF8);
  2. 引用
int &a //定义的是a的引用:这样定义的变量的地址会与原变量的地址相同,就是一个地址同时指向了两个变量

引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作。

  1. 在C语言中,可以使用库函数malloc()来分配内存,而在C++中有更好的方法-new操作符。
int* p = new int; //表示分配int大小的内存
delete p;
  1. 类定义
class 类名
{
    访问修饰符:
        变量;
        方法;
}; //分号结束一个类
  1. 类的构造函数:一种特殊的方法,它会在每次创建类的新对象时执行。构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回void
  2. 继承 当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类
class 派生类: 继承类型 基类
  1. 类访问修饰符
  • public:公有成员在程序中类的外部是可访问的
  • private:私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
  • protected:protected(受保护)成员变量或函数与私有成员十分相似,但有一点不同,protected(受保护)成员在派生类(即子类)中是可访问的。
  1. 类的多继承:多继承即一个子类可以有多个父类,它继承了多个父类的特性
class <派生类>:<继承类型> <基类1>, <继承类型> <基类2>...

示例代码:

#include <iostream>

using namespace std;

// 基类 Shape
class Shape 
{
   public:
      void setWidth(int w)
      {
         width = w;
      }
      void setHeight(int h)
      {
         height = h;
      }
   protected:
      int width;
      int height;
};
 
// 基类 PaintCost
class PaintCost 
{
   public:
      int getCost(int area)
      {
         return area * 70;
      }
};
 
// 派生类
class Rectangle: public Shape, public PaintCost
{
   public:
      int getArea()
      { 
         return (width * height); 
      }
};
 
int main(void)
{
   Rectangle Rect;
   int area;
 
   Rect.setWidth(5);
   Rect.setHeight(7);
 
   area = Rect.getArea();
   
   // 输出对象的面积
   cout << "Total area: " << Rect.getArea() << endl;
 
   // 输出总花费
   cout << "Total paint cost: $" << Rect.getCost(area) << endl;
 
   return 0;
}
  1. 重载:C++允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。
#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;
}
  1. 多态:多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
  2. 数据抽象:只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。数据抽象是一种依赖于接口和实现分离的编程(设计)技术。
  3. 数据封装:一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。
  4. 接口:C++ 接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念。如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 "= 0" 来指定的。
virtual double getVolume() = 0;

设计抽象类(通常称为 ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。抽象类不能被用于实例化对象,它只能作为接口使用。例如,基类 Shape 提供了一个接口 getArea(),在两个派生类 Rectangle 和 Triangle 中分别实现了getArea()

  1. 构造函数和析构函数
  • 构造函数的作用:建立对象时对对象的数据成员进行初始化
  • 析构函数的作用:对象被释放的时候执行
  1. 如果方法需要引用整个调用对象,则可以使用表达式*this。其中this指针指向调用对象。
  2. 对象数组:类名 数组名[长度];
  3. 访问者模式:
//C++访问者模式

#include<iostream>
#include<string>
#include<list>
#include<windows.h>

using namespace std;

class Apple;
class Book;

//抽象访问者
class Visitor {
    public:
        void set_name(string name) {
            name_ = name;
        }

        virtual void visit(Apple *apple) = 0;
        virtual void visit(Book *book) = 0;

    protected:
        string name_;
};

//具体访问者:顾客
class Customer : public Visitor {
    public:
        void visit(Apple *apple) {
            cout << "顾客" << name_ << "挑选苹果" << endl;
        }

        void visit(Book *book) {
            cout << "顾客" << name_ << "买书" << endl;
        }
};

//具体访问者:收银员
class Saler : public Visitor {
    public:
        void visit(Apple *apple) {
            cout << "收银员" << name_ << "给苹果称重并计算价格" << endl;
        }

        void visit(Book *book) {
            cout << "收银员" << name_ << "计算书的价格" << endl;
        }
};

//抽象被访问者
class Product {
    public:
        virtual void accept(Visitor *visitor) = 0;
};

//具体被访问者:苹果
class Apple : public Product {
    public:
        void accept(Visitor *visitor) override {
            visitor->visit(this);
        }
};

//具体被访问者:书
class Book : public Product {
    public:
        void accept(Visitor *visitor) override {
            visitor->visit(this);
        }
};


//商场
class Market {
    public:
        void accept(Visitor *visitor) {
            for(auto prd : product_list) {
                prd->accept(visitor);
            }
        }

        void addProduct(Product *product) {
            product_list.push_back(product);
        }

        void removeProduct(Product *product) {
            product_list.remove(product);
        }
    
    private:
        list<Product*> product_list;
};

int main()
{
    SetConsoleOutputCP(CP_UTF8);
    //被访问者实例对象
    Book book;
    Apple apple;
    //商场实例对象(管理员类)
    Market market;

    //商场货物登记
    market.addProduct(&book);
    market.addProduct(&apple);

    //访问者实例对象
    Customer customer;
    Saler saler;

    customer.set_name("张菁");
    saler.set_name("徐菁");
    
    //启动访问者模式
    market.accept(&customer);
    market.accept(&saler);

    return 0;

}