C++:02.类与对象:struct于class,拷贝构造,静态成员,常对象,嵌套类,友元,类模板

struct与class:

在C语言中struct定义结构体,在C++中struct与class都是定义类,区别在struct默认public,class默认private。还有一种说法:没有成员函数的 struct 还是称作“结构”,结构变量不是对象;有成员函数的 struct 就是类。

在.c文件中访问结构体,不可以省略struct

typedef struct Node
{
	int data;
	struct Node *next;//不可以省略struct
}Node,*Pstack;

但是再.cpp文件中struct也是类。可以省略

 并且.c文件中空结构体,在VS中不能通过编译,在gcc下空结构体的大小为0。在.cpp中空类的大小为1,因为类中有默认的构造函数等,调用合适的构造函数CGoods(CGoods *this)肯定会有this指针,而为了放这个指针则肯定会至少有一字节空间。

一般来说:一个对象占用的内存空间的大小等于其成员变量所占用的内存空间的大小之和,也存在内存对齐。


类成员的访问范围:

private:用来指定私有成员。私有成员,不论是成员变量还是成员函数,都只能在该类的成员函数内部才能被访问。

public:用来指定公有成员。一个类的公有成员在任何地方都可以被访问。

protected:用来指定保护成员。这需要等介绍“继承”之后再解释。

尤其注意private:设置私有成员的机制叫作“隐藏”。“隐藏”的一个目的就是强制对成员变量的访问一定要通过成员函数进行。这样做的好处是易于代码的维护。并且让不希望对外开放的成员隐藏起来。


构造函数:

对象进行自动初始化。如果没有自定义的构造函数,会使用默认构造函数,默认构造函数什么都不干。如果有了自定义的构造函数,编译器就不会生成默认构造函数。

定义一个对象:1、开辟空间  2、调用构造函数       所以说构造函数执行时,对象的内存空间已经分配好了,构造函数的作用只是初始化这片空间。

构造函数可以添加任意的形参变量,所以构造函数可以重载。

调用对象默认构造函数的时候,不能在对象后面添加空的(),成函数声明了。()里有内容是可以的,直接调用有参数的构造函数。


拷贝构造函数:

调用:

一般加const,这样能让常量对象作为参数,非常量对象也可以。
必须有&,没有的话是值传递,在传参的时候,同样会调用拷贝构造,造成递归循环。并且值传递调用函数会产生
开销,所以直接用引用就好。同时传引用的缺点在于,如果形参改变,实参也会改变,所以我们加const确保实参
的值不会改变。
Complex(const Complex & c)
{
    real = c.real; imag = c.imag;
    cout<<"Copy Constructor called"<<endl ;
}

Complex c1(1,2);
Complex c2 (cl);调用拷贝构造。与Complex c2 = cl(初始化);是一个意思。

Complex c3;
c3 = c1;调用赋值(=)运算符重载。(赋值)

同样没有自定义的拷贝构造就会调用默认的拷贝构造,有自定义的就没有默认的了。

注意:1、由于拷贝构造是自己写的,所以实际上它是否真的是完全拷贝,取决于写代码的人怎么写的拷贝构造函数。

           2、对于一般函数来说,如果参数是类对象,调用该函数时,也会调用拷贝构造函数。

           经过上述两点,会导致函数的形参的值并不一定等于函数调用时实参的值。


析构函数:

一个类有且仅有一个析构函数。

同样如果没写析构函数,则编译器生成默认析构函数。如果定义了析构函数,就不生成默认析构函数。

析构函数在对象消亡时即自动被调用。

先构造的后析构,后构造的先析构


静态成员变量和静态成员函数:

class CRectangle
{
public:
    static void PrintTotal ();  //静态成员函数
private:
    int w,h;
    static int nTotalArea;  //静态成员变量
    static int nTotalNumber;  //静态成员变量
};


int CRectangle::totalNumber = 0;
int CRectangle::totalArea = 0;
类的静态成员变量必须在定义类的文件中对静态成员变量进行一次声明或初始化,否则编译能通过,链接不能通过。

普通成员变量:每个对象各自有一份。访问普通成员时,要通过对象名.成员名等方式。

静态成员变量:只有一份,被所有同类对象共享。所以在处理只需要一份的成员变量的时候设置为静态的,例如:单例模式。访问静态成员时,则可以通过类名::成员名的方式访问。不需要指明被访问的成员属于哪个对象或作用于哪个对象。因此,甚至可以在还没有任何对象生成时就访问一个类的静态成员。静态成员变量并不是全局变量,私有类型同样不能再成员函数外访问。设置静态成员的目的就是对齐进行封装,更容易理解和维护。

普通成员函数:一定是作用在某个对象上的。

静态成员函数:并不具体作用在某个对象上。访问静态成员函数两种方式都可以,没有区别。因为静态成员函数不具体作用于某个对象,所以静态成员函数不能访问非静态成员变量,也不能调用非静态成员函数。只能访问静态的。静态成员函数没有this指针。

静态成员函数和变量都是独立于类的实例对象之外的。

使用 sizeof 运算符不会将静态成员变量计算在内。对上面的 CRectangle 类来说,sizeof(CRectangle) 的值是 8。


常对象和常成员函数:

常对象:对象的值初始化之后就不会在被改变。在定义对象时可以加const。

常对象无法调用普通成员函数,只能调用常成员函数(函数加const)。同样常函数也不能使用其他普通成员变量。

两个成员函数的名字和参数表相同,但一个是 const 的,一个不是,则它们算重载。

class Sample
{
public:
    void GetValue() const;  //常成员函数
};
void Sample::GetValue() const  //常成员函数
{
}
int main()
{
    const Sample o;
    o.GetValue();  //常量对象上可以执行常量成员函数
    return 0;
}

基本上,如果一个成员函数中没有调用非常量成员函数,也没有修改成员变量的值,那么将其写成常量成员函数是好的习惯。

补充:

在C中const定义的是常变量,不可以定义数组。除了不能做左值,其他与一般变量一致。
不能直接更改值,但可以通过访问内存来改值

在C++中const定义的常量,可以定义数组
编译方式:所有使用常量名字的地方,都被常量的值替换了

const: 常变量  就看初始值是不是一个立即数 10 20 30
eg:int a = 10;
       coust int b = a;常变量

       coust int b = 10;常量

       const修饰的  常量&常变量  保证它不能被直接或者间接的修改


封闭类:

一个类的成员变量是另一个类的对象。例如:A类的成员有B类的对象。

初始化的时候如果初始化A类对象,也会对B类对象初始化。我们使用初始化列表进行初始化。

class CTyre  //轮胎类
{
public:
    CTyre(int r, int w) : radius(r), width(w) { }
private:
    int radius;  //半径
    int width;  //宽度
};

class CCar   //汽车类  封闭类
{  
public:
    CCar(int p, int tr, int tw);
private:
    int price;  //价格
    CTyre tyre;
};

CCar::CCar(int p, int tr, int tw) : price(p), tyre(tr, tw)//参数列表
{
};

int main()
{
    CCar car(20000, 17, 225);
    return 0;
}

封闭类对象生成时,先执行所有成员对象的构造函数,然后才执行封闭类自己的构造函数。成员对象构造函数的执行次序和成员对象在类定义中的次序一致,与它们在构造函数初始化列表中出现的次序无关。所以初始化链表成员之间不要相互依赖。

当封闭类对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数。

对于封闭类来说,如果涉及到类模板,那么再写封闭类里的函数的时候注意成员对象加上<T>。

 


嵌套类:

一个类里定义了另一个类

template< typename T >
class Link
{
public:
    Link();
    ~Link();
    void insert(const T &val);
    void remove(const T &val);
    void show();
private:
    // 嵌套类
    struct Node
    {
        给了默认值的构造函数,T()保证了T不论是哪个类型,初始化都是0,float是0.0,bool是false。这样不需再写默认构造函数
        Node(T val = T()):_data(val),_next(NULL){}
        T _data;
        Node *_next;
    };
    // 指向头结点的指针
    Node *_head;
};

 Node(T val = T()):_data(val),_next(NULL){}

T()保证了T不论是哪个类型,初始化都是0。这是一个很巧妙的方法。


友元函数和友元类:friend

友元函数:1、声明全局函数为一个类中的友元,则这个全局函数可以使用这个类中的私有成员变量。

                  2、声明类A中的函数为类B中函数的友元函数,则该函数可以使用类B的私有成员变量。

class B;因为类A中使用了类B,所以需要提前声明类B。
class A
{
public:
    void Fun(B *p);
private:
    int num;
};

class B
{
private:
    int a;
    int b;
    friend void A::Fun(B *p);标明所属类。
};

void A::Fun(B *p)必须在类外定义   并且在访问类B的时候需要通过指针
{
    cout << num;说明这个是有this指针的。
    cout << p->a << p->b <<endl;不清楚是什么原因,在VS中显示标红(说你使用了类B私有成员),但其实不会发生错误
}

为什么必须在类外定义:
    如果在类A中定义,但这时候你类B并没有定义,会报错。
    如果在类B中定义,你函数都说了是类A的函数,咋能在类B定义,会报错。

友元类:类 A 可以将类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员。

class A
{
private:
    int num;
    friend class B;  //声明 B为友元类
};

class B
{
public:
    void Fun() 
    {
        a.num += 1000;  因B是A的友元类,故此处可以访问类A的私有成员
    }
private:
    A a;
};

注意:

  1. 友元关系不能被继承。
  2. 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元。
  3. 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元。

 类模板:

意义:可以定义类型变量,来接收用户传入的类型

template<> : 定义模板参数列表
                               <>里:typename T, typename E, typename R: 类型变量    用,号分割

类模板  =》  实例化   =》  模板类

注意:模板的代码,在没有实例化之前,是不编译的。

类模板的选择性实例化   实例化哪部分,编译哪部分  可以通过实例化检查是否有错误。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章