char盲区
2014年4月01日 00:29
运行结果:
Happy coding
运行结果:
在基类中的private成员,不能在派生类中任何地方用using声明。
在基类中的protected成员,可以在派生类中任何地方用using声明。当在public下声明时,在类定义体外部,可以用派生类对象访问该成员,但不能用基类对象访问该成员;当在protected下声明时,该成员可以被继续派生下去;当在private下声明时,对于派生类定义体外部来说,该成员是派生类的私有成员。
在基类中的public成员,可以在派生类中任何地方用using声明。具体声明后的效果同基类中的protected成员。
例:
class A
{
public:
int f1;
protected:
int f2;
private:
int f3;
};
//如下声明只是举例,实际声明中不能重复声明同一成员。
class B : public A //这里的访问标号可以是任意,它只影响没有被显示声明的从基类继承的成员的访问
{
public:
using A::f1; //从基类继承的public成员,此处声明后可以被外部访问
using A::f2; //从基类继承的protected成员,此处声明后可以被外部访问
using A::f3; //声明错误,派生类不可访问基类的private成员,即使声明也不行
protected:
using A::f1; //从基类继承的public成员,此处声明后可以被下级派生类访问,但不能被外部访问
using A::f2; //从基类继承的protected成员,此处声明后可以被下级派生类访问,但不能被外部访问
using A::f3; //声明错误,派生类不可访问基类的private成员,即使声明也不行
private:
using A::f1; //从基类继承的public成员,此处声明后既不能被下级派生类访问,也不能被外部访问
using A::f2; //从基类继承的protected成员,此处声明后既不能被下级派生类访问,也不能被外部访问
using A::f3; //声明错误,派生类不可访问基类的private成员,即使声明也不行
};
总结:可被派生类访问的基类成员,都可以在派生类中的任何访问标号下用using声明,对于派生类外部来说,被声明成员的访问权限由using声明所在的访问标号决定,与基类中的访问权限无关,也与派生列表中的访问标号无关(转自lfw帖)
#include <iostream>
using namespace std;
class Student
{
public:
virtual void print()
{
cout << "A student!" << endl;
}
};
class Gstudent:public Student
{
public:
virtual void print()
{
cout << "G student!" << endl;
}
};
int main()
{
Student s1;
Student *ps;
Gstudent s2;
s1.print();
s2.print();
s2.Student::print();
ps = &s1;
ps->print();
ps = &s2;
ps->print();
ps->Student::print();
return 0;
}
1
1.析构函数的作用并不是删除对象,即不能收回对象本身所占用的静态内存空间(其是由系统释放),而是在系统删除对象之前完成一些工作,如回收在运行过程中动态申请的内存空间。Ex:在用new创建对象,可以在析构函数中用delete进行删除。
2.析构函数不能重载,一个类中只能定义一个析构函数。
3.static局部对象在函数调用结束时对象并不释放,因此不调用析构函数,只在main函数或调用exit函数结束程序时才调用static局部对象的析构函数。如果是全局对象,则调用该全局对象的析构函数。
4.如果用new运算符动态创建一个对象,当用delete释放该对象时,先调用该对象的析构函数。
5.析构函数是可以被对象调用的,而构造函数是不可以被对象调用的,构造函数只能被系统调用。
1
1.重载函数集合中的全部函数都应该在同一个域中,一个声明为局部的函数将隐藏一个全局域中声明的函数,而不是重载一个全局域中声明的函数。
2.如若把const应用在指针或引用参数指向的类型上,则在判断函数声明是否相同时就要考虑const修饰符。
//声明了不同的函数,是重载函数
void fun(int *);
void fun(const int *);
//声明了不同的函数,是重载函数
void fun(int &);
void fun(const int &);
//是相同的函数,编译提示:同一函数定义两次
void fun(int);
void fun(const int);
1
1
1
1
1.静态成员函数不能声明为虚函数,因为静态函数不属于某一个对象,没有多态性的特征。
2.构造函数不能是虚函数,虚函数作为运行时的多态性的基础,主要是针对对象的,而构造函数是在对象产生之前运行的。
3.内敛成员函数不能声明为虚函数,如果将在类声明时就定义内容的成员函数声明为虚函数,此时不是内敛函数。
4.析构函数往往被定义为虚函数。若类中有虚函数,则其析构函数更应当定义为虚函数。由其派生的所有子类的析构函数也是虚函数。
5.如果在派生类中没有重新定义虚函数,则派生类的对象将使用基类的虚函数代码。
6.重新定义虚函数时,要求返回类型、函数名、参数类型、参数个数、参数顺序与基类中函数完全相同,有一种函数覆盖的感觉。
7.纯虚函数即是将虚函数赋值0,包含纯虚函数的类成为抽象类。
8.抽象类处于类层次的最上层,一个抽象类自身无法实例化,即无法定义一个抽象类的对象,只能通过继承机制生成抽象类的非抽象派生类,然后再实例化。其专门作为基类派生新类。
9.纯虚函数是为了解决在基类中无法实现的函数,而在派生类中再给出函数的具体实现,它只在基类中说明函数原型用来规定整个类簇的统一接口形式。但要求任何派生类根据实际需要都要定义自己的实现方法,即基类不给出函数实现部分,而在派生类给出。
10.纯虚函数与函数体为空的虚函数是有区别的。1)没有函数体;2)不能实例化,即不能声明对象;3)都具有多态性。
11.抽象类不能声明抽象类的对象,故抽象类不能用作参数类型、函数值类型或显式转换的类型,但可以声明指向抽象类的指针或引用,通过指针或引用就可以指向并访问派生类的对象,进而访问派生类成员实现多态性。
12。如果派生类给出了所有纯虚函数的实现,那其就不再是抽象类,否则仍是抽象类。
1
1.virtual关键字只用在虚函数的声明中,在类外定义虚函数的时候不能用virtual。为了提高程序可读性,往往在派生类定义该函数时不省略virtual。
2.静态联编,当基类与派生类有同名函数时,调用的时候是根据类型来判断调用不同类中的同名函数,在编译阶段完成;动态联编,当将基类中与派生类同名的函数定义为virtual时,当用指针调用的时候是根据对象来判断调用不同类中的同名函数,在运行阶段完成。
3.虚函数在每个类中建议都有virtual说明,并且其对于每个类都有不同的函数定义。
4.只有通过对象指针或对象引用来调用虚函数,才能实现动态联编,如果使用对象来调用虚函数,则采用的是静态联编。
5.虚函数派生下去仍是虚函数,可以省略virtual,但不建议。
6.虚函数与虚拟继承有相似的地方,但它们之间没有任何联系。虚拟继承即虚基类实现。
7.通常将类簇中的具有共性的成员函数声明为虚函数,而具有个性的函数没有必要声明为虚函数。但也有类外
1.派生类不继承基类的构造函数和析构函数。
2.派生类构造函数只能合法地调用其直接基类的构造函数,不能调用非直接基类的构造函数。
3.当基类的构造函数使用一个或多个参数时,派生类必须定义构造函数,提供将参数传递给基类构造函数的途径。
4.UML:统一建模 语言,+:public;-:private;#:protected。
5.多继承派生类的构造函数;<派生类名>(所有参数列表包括继承基类的构造函数):<基类名1>(基类参数列表),<基类名2>(基类参数列表)…
6.引进虚基类,派生类对象中只存在一个虚基类成员的副本。class 派生类名:virtual public 基类名。其实际与虚基类中的虚成员函数是一样的效果。虚基类目前看到用于多派生类直接继承后,又有派生类多继承后解决基类数据多次被复制问题。
7.同一层次上派生类继承虚基类和非虚基类,则不管定义派生类时所指定的基类顺序,先调用虚基类构造函数再调用非虚基类构造函数。虚基类只允许定义不带参数或带默认参数的构造函数(原因是虚基类首先被调用,这也只能被调用到默认构造函数了,当虚基类定义了别的构造函数,那么同时需要提供默认构造函数?),如果多继承不牵扯到对同一基类的派生就不用第一虚基类,虚基类只在派生类继承的时候才体现出来。
1
1
1
1
1
1.默认构造函数,<类名>(){<必须对数据成员进行初始化>};与<类名>(<带默认值的全部数据成员>);是一样的,二者只能取其一。另外,构造函数中的参数名与类中数据成员名尽量不要相同。如若相同需要用到this指针。
2.全部带默认参数的构造函数,必须对每一个数据成员提供默认参数值。这样可以如此声明:Member m;当定义了全部带默认参数的构造函数时就不能再定义默认的构造函数。Member(){};
3.默认构造函数是一个无参构造函数,它仅负责创建对象,不做任何初始化工作,这样类中数据成员的值是不定的(当类对象被定义在局部域中,若是全局域,则类中数据成员被系统初始化),只要类定义了一个构造函数,系统就不提供默认构造函数。
构造函数不能声明为 const,const 构造函数是不必要的。创建类类型的 const 对象时,运行一个普通
构造函数来初始化该 const 对象。构造函数的工作是初始化对象。不管对象是
否为 const,都用一个构造函数来初始化化该对象。从概念上讲,可以认为构造函数分两个阶段执行:(1)初始化阶段;(2)
普通的计算阶段。计算阶段由构造函数函数体中的所有语句组成。
不管成员是否在构造函数初始化列表中显式初始化,类类型的
数据成员总是在初始化阶段初始化。初始化发生在计算阶段开
始之前。没有默认
构造函数的类类型的成员,以及 const 或引用类型的成员,不
管是哪种类型,都必须在构造函数初始化列表中进行初始化。记住,可以初始化 const 对象或引用类型的对象,但不能对它们赋值,在
开始执行构造函数的函数体之前,要完成初始化。初始化 const 或引用类型数
据成员的唯一机会是构造函数初始化列表中.
使用构造函数初始化列表:必须对任何 const 或引用类型成员以及没有默认构造
函数的类类型的任何成员使用初始化式。
考虑下面的类:
class X {
int i;
int j;
public:
// run-time error: i is initialized before j
X(int val): j(val), i(j) { }
};
在这种情况下,构造函数初始化列表看起来似乎是用val 初始化 j,然后再
用 j 来初始化 i。然而,i 首先被初始化。这个初始化列表的效果是用尚未初
始化的 j 值来初始化 i!按照与成员声明一致的次序编写构造函数初始化列表
是个好主意.一般情况下,通过(重复)使用构造函数的形参而不是使用对象的数据成员,
可以避免由初始化式的执行次序而引起的任何问题
只要定义一个对象时没有提供初始化式,就使用默认构造函数,为所有形参提供默认实参的构造函数也定义了默认构造函数。
合.一个类哪怕只定义了一个构造函数,编译器也不会再生成默认构造函数。
NoDefault 没有默认构造函数,意味着:
1. 具有 NoDefault 成员的每个类的每个构造函数,必须通过传递一个初始
的 string 值给 NoDefault 构造函数来显式地初始化 NoDefault 成员。
2. 编译器将不会为具有 NoDefault 类型成员的类合成默认构造函数。如果
这样的类希望提供默认构造函数,就必须显式地定义,并且默认构造函数
必须显式地初始化其 NoDefault 成员。
3. NoDefault 类型不能用作动态分配数组的元素类型。
4. NoDefault 类型的静态分配数组必须为每个元素提供一个显式的初始化
式。
5. 如果有一个保存 NoDefault 对象的容器,例如 vector,就不能使用接受
容器大小而没有同时提供一个元素初始化式的构造函数。
实际上,如果定义了其他构造函数,则提供一个默认构
造函数几乎总是对的。
Time t(3,20,54),t1=t;//先调用定义的构造函数,然后t1=t or t1(t),调用系统的复制构造函数
Time t(3,20,54),t1;//调用定义的构造函数两次,将t1看作是使用定义的默认参数的构造函数
t1 = t;//是对象的赋值
C++编译器自动为类生成 1)默认构造函数;2)默认复制构造函数;3)默认赋值运算符重载函数*只有一个*。当一个类的数据成员中定义有指针,则一定要重新定义复制构造函数,否则一定会出错,因为存在两个对象同时指向了同一资源,其称为“浅复制”,如果类需要析构函数来析构资源时,则也需要显示定义一个复制构造函数来进行深复制。注意赋值与初始化是有区别的,初始化是在类创建的时候进行的,仅此一次。
Time t;
t = Time(3,20,54);//创建无名对象,并通过赋值运算符重载把无名对象数据成员的值赋给对象t,然后系统自动调用析构函数,释放无名对象占用的资源后再删除无名对象。
1
1
1.常量
声明格式:const <数据类型> <常量名> = <表达式>;常量定义时必须初始化。
2.常指针
声明格式:<数据类型> *const <指针名> = <地址>;指针本身的值不能改变。
3.指向常量的指针
声明格式:const <数据类型> *<指针名> = <常量值>;指针指向的值不能改变。
4.常引用
声明格式:const <数据类型> &<引用名>;常为函数参数,目地保护实参
5.常对象
声明格式:const <类名> <对象名> = <初始化值>; 或 const <类名> <对象名>(<初始化值>);
1)它的数据成员(公有or私有)的值在对象的生存期不能改变,故必须进行初始化,且不能更新。
2)由于无法预料哪些成员函数会改变数据成员的值,故不能用常对象调用普通成员函数,但可调用常成员函数。
3)如若需要改变常对象中某个数据成员值,可将其声明为mutable,如:mutable int count;修改其值是通过调用声明为const的常成员函数的。
6.常数据成员
声明格式:const int <变量名>; 构造函数通过成员初始化列表对其初始化。
7.常成员函数
声明格式:<数据类型> <函数名> (<参数列表>) const; 在声明和定义时都要有const,调用时不必。另外其不能调用其他非const成员函数。
数据成员 | 非const成员函数 | const成员函数 |
非const数据成员 | 可引用,可改值 | 可引用,不可改值 |
const数据成员 | 可引用,不可改值 | 可引用,不可改值 |
const对象的数据成员 | 不允许引用 | 可引用,不可改值 |
通常把不允许修改数据成员的函数设置为常成员函数,把不允许修改成员的对象设为常对象,常对象只能访问常成员函数。注意:const可以实现成员函数重载。如:void print();-->普通对象调用;void print() const;-->常对象调用。
Eg:
(1) const int* p = &a;
(2) int* const p = &b;
(3) const int* const p = &c;