面向对象学习笔记(避雷篇)

发布时间 2023-09-24 15:58:41作者: 一铭君一

面向对象学习笔记(避雷篇)

时间还挺快的,当年的 OIer 已经上大学啦!在大学的 C++ 学习中,要求我们掌握两种编程技巧,即面向对象和面向过程。

因为高中时期不管是 CSP 还是 NOIp 的比赛,大家用的基本上都是面向过程的设计思路,因此我开学后就着重开始学习面向对象的方法。

这里对面向对象的概念不做细致的讨论了。总之,复杂的也用不到,现在的水平只能做到 “封装” 这一项。

下面来整理一下最近写代码时候遇到的困难和解决办法。

在类外调用类内的静态函数

大多数时候,我们要指定特定的某一个对象去调用 public 中的函数,但是也有一些情况,不可避免的要直接调用类内 public 中的函数,这时候可以使用静态函数的方法。

class Example
{
  static void test()
  {
    //do something ...
  }
};

//在其他函数中

Example::test();
  • 原理:
    static 关键词表示这是类内公用的一个函数/变量(静态函数/变量),它不属于类内的任意一个对象。因此,可以跳过指定对象的环节直接调用。

在类外调用类内的静态变量

类似上面,我们有一个公共的变量 a 。不管定义了多少个对象,它们都公用这一个变量 a ,相应的,变量 a 也只占对应变量类型的空间大小,不会随对象的增多而占用更多的空间。

在类内的函数中,可以直接调用 a

在类外的函数中,可以使用 classname::a 来调用变量 a

class Example
{
  static int a;
  void test1()
  {
    std::cout << a;
  }
};

void test2()
{
  std::cout << Example::a;
}

int Example::a;
// 这一句很重要,下面讲为什么

需要注意的是,在类内写出 static int a; 这样的句子不被视为 “定义” 行为,而是 “声明” 行为。因为 astatic 属性,导致它不属于任意一个对象,所以在开内存的时候,它不能占用任意一个对象的内存,也不能被构造函数初始化。

所以,需要在类外,再次单独给出一次 a 的 “定义”,分配给它一个内存。如果需要的话,也要单独的初始化它。这样才能正常地调用静态变量 a

如果不在类外重新定义 a 的话,编译器会报错 "undefined symbol" 。

在类外强行访问 private 中的元素

尽管这有违面向对象的初衷——保护对象中的元素不能直接从外部访问,但是有时候我们不得不这样做,这就用到了友元关键词 friend

关键词 friend 可以授权其他的类或者函数直接访问该类中的 private 或者protected 中的元素。

class Example
{
  public:
    friend void test1();
  private:
    int a;
}e;

void test1()
{
  std::cout << e.a;
}
//test1() 可以运行,因为 friend 关键词授予了它访问 private 元素的权限

void test2()
{
  std::cout << e.a;
}
//test2() 不能运行,报错显示无权访问 private 中的元素

构造函数需要传参

如果使用构造函数初始化的话,那么大概率需要传参,但是参数又大多都是用户输入的,这就会导致无法把对象定义为全局变量(因为不知道参数,定义的时候会因为构造函数中没有参数而报错)。

一种显而易见的解决方法是不要用构造函数初始化,自己另写一个带 static 关键词类型的初始化函数,然后在主函数中调用。

如果自带反骨,一定要用带参数的构造函数初始化,并且还想开全局变量的话,也有办法:使用 std::vector 。因为 vector 容器的初始长度为 0 ,不占用任何内存,所以声明的时候不会调用构造函数,避免了报错。

在主函数中得到数据之后,可以再把数据打包起来然后用构造函数加 pushback() 函数塞到类里面。

class Example{
  public:
    Example(const int A[]){
      for(int i = 0 ; i < 3 ; i ++)
        a[i] = A[i];
    }
  private:
    int a[3];
};

std::vector < Example > V;

int main()
{
  int a[3];
  for(int i = 0 ; i < 3 ; i ++)
    std::cin >> a[i];
  V.push_back( Example(a) );
  return 0;
}

构造函数的初始化列表

用初始化列表来写构造函数会让代码更整洁!

class Example
{
  public:
    Example(int A, int B) : a(A), b(B){}
  /*
    等价于
    Example(int A, int B)
    {
      a = A;
      b = B;
    }
  */
  private:
    int a, b;
};

另外有个顺序问题,详情可以看这篇 blog