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++ 学习笔记:类相关

C/C++ 学习笔记:类相关 重载、重写、隐藏,虚函数等等

C++之相关学习笔记

1.继承基类时,基类构造函数带参数 子类构造函数():基类(参数),子类参数 2.函数覆盖(发生在父类和子类之间) 函数重载(发生类之间) 3.类型转换 内存匹配 4.c++多态性(virtual 子...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

boolan——c++学习笔记之类关系

类的的三种关系:1)继承(inheritance)2)复合(composition)3)委托(delegation) 1)继承(inheritance) 1、继承关系表现 2、继承关...

c#学习笔记

c#学习笔记 博客分类:  c# c#  示例程序 using System; class HelloWorld { static string say = “He...

第二篇 C#变量表达式

1.各种常用变量类型。 整数类型: 浮点数类型: decimal类型: bool类型: 其他的类型: 2.字符串字面值 (1)使用转义序列。 (2)逐字地指定字符串...

C++学习笔记1

1.  操作符的含义——该操作符执行什么操作以及操作结果的类型——取决于操作数的类型。   2.  数组通过指针赋值 int ia[10]; ia[0] = 0;          ...
  • wzhiu
  • wzhiu
  • 2014-07-01 18:04
  • 871

C_C++软件工程师就业求职手册学习笔记---第三章

第三章程序基础 3.1 变量赋值  变量的赋值操作主要两种,一是 = ,另一种是 ++,--。对于内存操作主要是读和写。 例子1:          x*=(y=z=3),这个语句的执行顺序是从右到左...

C++学习笔记2

1. C++ 没有明确定义如何释放指向不是用new 分配的内存地址的指针。下面提供了一些安全的和不安全的delete expressions 表达式。  int i; 242 int *pi =...
  • wzhiu
  • wzhiu
  • 2014-07-02 14:49
  • 818

C++学习——继承

C++的继承是类的重要特性。

C++笔记1

1、用#define声明一个常数,用以表明1年中有多少秒? #define SECONDS_PER_YEAR (60 * 60 * 24 *365)UL UL:无符号长整型 2、类成员的初始化,初始化...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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