C/C++ 学习笔记:类相关

原创 2016年08月28日 15:13:48

构造和析构次数

A* p =new A[5];//  构造五次

delete[ ] p;//  析构五次

delete p;// 若不使用[ ],则析构一次。


假设 A 是一个类,像这样 A a(); 并不是表示调用构造函数创建对象,只是定义了一个函数而已。


绝不重新定义继承而来的缺省参数


若父类没有无参的构造函数,子类需要在自己的构造函数中显式调用父类的构造函数。


构造函数中可以调用虚函数

class Base {
public: 
    Base() { Function();}
    virtual void Function() { cout << "Base::Function" << endl;}
}; 
class A :public Base {
public: 
    A() { Function();} 
    virtual void Function() { cout << "A::Function" << endl;}
};

另外:这样定义一个 A 的对象, A a;  会输出

Base::Function

A::Function

 而不是

A::Function

A::Function

要知道在 Base 的构造函数中调用 Fuction 的时候,调用的是 Base 的 Function。因为此时 A 还没开始构造。



静态成员变量可被该类的所有方法访问。


子类型必须是子类继承了父类的所有可继承特性,也即公有继承,才能说是子类型,否则就只是单纯的子类



重载、重写、隐藏
1. 重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。(它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
2. 重写(覆盖):是指子类重新定义父类虚函数的方法。其函数名、参数列表、返回值类型,所有都必须同基类中被重写的函数一致,只有函数体不同(花括号内)。(和多态真正相关
3. 隐藏:隐藏是指派生类的函数屏蔽了与其同名的基类函数。注意只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。(和多态无关
重载和重写的区别

1). 范围区别:重写和被重写的函数在不同的类中,重载和被重载的函数在同一类中。

2). 参数区别:重写与被重写的函数参数列表、返回类型一定相同;重载和被重载的函数参数列表一定不同,不关心函数返回类型。

class A {
    int function(int i);
    int function(int i) const; // 第一个函数的重载
    int function(const int i);// 编译错误,这里不是重载第一个函数
};

分析

首先 int function(inti)const; 这个函数的形参表和第一个函数是不一样的,因为在类中隐含this 形参的存在这里加上 const 修饰的是 this 指针,则 this 指针的类型就变为指向const 对象的指针。因此,使用指针传参时,指向const  对象的指针和指向非const  对象的指针做形参的函数是不同的。

然后 int function(constint i); 这个函数无法重载第一个函数,因为这个函数和第一个函数对于重载来说是等价的。对于非引用或指针传参,形参是否const  是等价的,但对于引用或者指针形参来说,有无const  是不同的。

3).virtual的区别:重写的基类必须要有virtual 修饰,重载函数和被重载函数可以被virtual 修饰,也可没有。

隐藏和重写,重载的区别

1). 与重载范围不同:隐藏函数和被隐藏函数在不同类中。

2). 参数的区别:隐藏函数和被隐藏函数参数列表可以相同,也可以不同,但函数名一定同;当参数不同时,无论基类中的函数是否被virtual 修饰,基类函数都是被隐藏,而不是被重写。

总的来说:隐藏和重写,要在不同的类中,如果不满足重写,那就是隐藏

 
友元函数最后面不能加 const ,在函数后面加 const  只适用于成员函数
 
虚函数
虚函数的底层是通过虚函数表实现的。
虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态成员函数(static

1. 直接继承

1)每一个具有虚函数的类都有 1 个虚函数表 VTABLE(如果子类有自己的虚函数,那么子类的虚表里面存放继承来的虚函数地址和自己虚函数的地址),里面按在类中声明的虚函数的顺序存放着虚函数的地址,这个虚函数表 VTABLE 是这个类的所有对象所共有的;

2)在每个具有虚函数的类的对象里面都有一个 VPTR 虚函数指针,这个指针指向它自己类的 VTABLE  的首地址,每个类的对象都有这么一种指针。

2. 虚继承

对于虚继承,若派生类有自己的虚函数,则它本身需要有一个虚指针,指向自己的虚表(虚继承的时候,子类自己的虚函数单独有个虚表,然后各个父类的的虚函数分别对应一个虚表)。另外,派生类虚继承父类时,首先要通过加入一个虚指针来指向父类,因此有可能会有两个虚指针。

类对象的大小 = 各非静态数据成员(包括父类的非静态数据成员)的总和+ vfptr指针(多继承下可能不止一个)+ vbptr指针(多继承下可能不止一个)+ 编译器额外增加的字节(对齐)。


C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?
虚析构函数的作用:当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。

 

多态需要通过父类的指针或者引用来实现

如果父类里的虚函数是 private 的,子类依然可以实现多态。虽然父类这个虚函数是 private,但,父类指针或引用可以通过自己的public 方法调用这个私有的虚函数。当然,子类重写的这个虚函数并不是继承来自这个父类的,但是可以实现多态。

 
多继承问题

class A {
public:
    int x;
    A(int X) : x(X) {}
    virtual void print() {
        cout << "A" <<endl;
    }
};
class B {
public:
    int x;
    B(int X) : x(X) {}
    virtual void print() {
        cout << "B" <<endl;
    }
};
class C : public A, public B {
public:
    int x;
    C() : A(1), B(2), x(3) {}
};
 
int main() {
    C c;
    cout << c.A::x << endl;// 输出 1
    cout << c.B::x << endl;// 输出 2
    cout << c.x <<endl;// 输出 3
 
    c.print();// 这里会编译错误,歧义。因为继承了 A 和 B 的 print,无法确定调用哪个。此时,若在 C 中定义了一个 print,就可以这样使用
 
    // 虽然 C 从 A 和 B 继承了 print 函数,但是由于 A 和 B 中都有 print,所以 C 的对象访问的时候要加上 A 或者 B 的域名
    c.A::print();// 输出 A
    c.B::print();// 输出 B
    A* pA = &c;
    B* pB = &c;
    pA->print();// 输出 A
    pB->print();// 输出 B
}


在重载 “+” 运算符中参数要加const,否则,连续相加会编译出错。同理 “- * / ” 也是。
class A {
    int val;
public:
    A(int v):val(v) {}
    friend const A operator+(const A& L, const A& R) {
        A temp(L.val + R.val);
        return temp;
    }
};
A a(1), b(2), c(3);
A d = a+b+c;
因为 operator+(返回值类型是A,所以 a b 求值后返回的是一个临时变量,接着这个临时变量再与 c 相加调用operator+(),临时变量不能修改,所以需要在形参里用const 引用的形式捕获。
返回值最好加上 const,否则,(b)= c这样变态的代码也不会编译出错。同理 “- * / ” 也是。

不能被重载的操作符有

.”   “::”   “?:”   “->”   “sizeof”   “#”(预处理符号)(小窍门:带点的操作符肯定不可以重载)


版权声明:本文为博主原创文章,转载请注明出处!

C++类的基本知识总结

最近看了C++ primer plus,学习了类,想写点什么给初学者,也算是一份收获: 首先,C++是OOP,既然这样,就要适合这种思维的编程思考,比如说:用class、方法等等这些。 先说class...
  • u012541747
  • u012541747
  • 2015年04月24日 17:41
  • 582

c语言调用c++类成员函数的方法

c语言调用c++成员函数的方法
  • Kevin_Mr
  • Kevin_Mr
  • 2015年10月16日 18:03
  • 3304

C++ 算法大全

第3章 控制语句  /* 1、打印出所有的“水仙花数”。所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身。例如,153是一水仙花数,因为153 = 13 + 53 + 33。 */ ...
  • sunmc1204953974
  • sunmc1204953974
  • 2014年09月16日 00:29
  • 3368

C/C++ 局部类和嵌套类

主要参考资料:C++编程思想和 高质量编程 以及网络文章 局部类 局部类是指在函数中定义类。 (c++不能在函数中定义函数。c++在类中定义的函数也就是成员函数。)这个类以及由它创建...
  • u010236550
  • u010236550
  • 2014年02月12日 14:15
  • 2709

【c/c++】复数类

#include using namespace std; class complex { public: complex(){ real = 0; imag = 0; } complex(dou...
  • lzm18064126848
  • lzm18064126848
  • 2016年01月04日 15:23
  • 1457

C++中类与对象的讲解(通俗易懂)

概念 描述 类成员函数 类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。 类访问修饰符 类成员可以被定义为 public、private 或 pro...
  • songkai320
  • songkai320
  • 2016年07月14日 20:09
  • 13226

C/C++语言实现K均值(C均值)聚类算法

K-means 算法的工作原理: 算法首先随机从数据集中选取 K个点作为初始聚类中心,然后计算各个样本到聚类中的距离,把样本归到离它最近的那个聚类中心所在的类。计算新形成的每一个聚类的数据对象的平...
  • luoluoxiaocainiao
  • luoluoxiaocainiao
  • 2013年06月27日 09:54
  • 3217

如何用C语言封装 C++的类,在 C里面使用

本文给出了一种方法。基本思想是,写一个 wrapper文件,把 C++类封装起来,对外只提供C语言的接口,和 C++i相关的都在  wrapper的实现文件里实现。 1. apple.h #ifn...
  • CaspianSea
  • CaspianSea
  • 2013年08月01日 01:02
  • 10279

c++史前时代与带类的C

c++史前时代 1979年,Bjarne Stroustrup,中文名挺拗口的,通常称之为BS,在贝尔实验室开始开发C++语言。为什么要发明新的语言呢?这涉及到作者之前的经历。那时,BS在剑桥读博士...
  • booirror
  • booirror
  • 2014年10月27日 00:20
  • 1797

详解C结构体、C++结构体 和 C++类的区别

先来说说C和C++中结构体的不同 a) C语言中的结构体不能为空,否则会报错 1>d:\myproject\visual studio 2013\projects\myc++\main.c(71): ...
  • Loving_Forever_
  • Loving_Forever_
  • 2016年05月23日 18:38
  • 16544
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C/C++ 学习笔记:类相关
举报原因:
原因补充:

(最多只允许输入30个字)