C++类简单示例

带参数的构造函数

 
#include <iostream> 
using namespace std;

class Triangle{
    private:
        int a;
        int b;
        int c;
    public:
        ~Triangle();
        Triangle(int a,int b,int c);
        void show();
        bool check();
};

void Triangle::show(){
    cout << "a=" << a << ",b=" <<b  << ",c="  << c  <<endl;
}

bool Triangle::check(){
    return a+ b > c;
}

//构造函数没有void,int之类的修饰符 
//如果方法参数与类变量重名,可以使用this区分
Triangle::Triangle(int a,int b,int c){
    this->a =a;
    this->b =b;
    this->c =c;
    cout  <<  "带参数构造函数生成具体的对象"  << endl;
}

//自动被调用
Triangle::~Triangle(){
    cout  << "对象占用空间被销毁"  << endl;
}


int main ()
{
    Triangle t1 = Triangle(3,4,5);
    t1.show();

    cout  << "是否为三角形:"  << t1.check()  << endl;
    return 0;
}

 
$ g++ c.cpp 
$ ./a.out 
带参数构造函数生成具体的对象
a=3,b=4,c=5
是否为三角形:1
对象占用空间被销毁

new创建对象

C++中的new和直接定义对象有以下主要区别

 
内存分配和释放:
使用new创建的对象需要手动释放,否则会导致内存泄漏。
而直接定义的对象在超出其作用域时会自动释放。


生命周期:
使用new创建的对象的生命周期可以手动控制,可以将其保留并在程序的其他部分使用。
而直接定义的对象的生命周期受局部作用域的限制。


存储位置:
使用new创建的对象存储在堆上,它们的生存期可以超出创建它们的作用域。
而直接定义的对象存储在栈上,它们在超出定义它们的作用域时会被自动销毁。


访问方式:
使用new创建的对象需要通过指针来访问和操作。
而直接定义的对象可以直接使用对象名称来访问和操作。


效率问题:
直接定义效率更高,频繁调用的场合并不适合new,总要去申请释放内存系统开销更大。


作用范围:
new出来的对象指针可以在方法之间传递,并且这个对象指针所指向的堆中的对象实例仍然存在,
直接声明的对象是局部的,出了方法就没有了。


需要注意的问题:
使用new创建对象时,应该在不再需要该对象时手动释放其内存,以避免内存泄漏。
同时,使用delete释放后,也需要小心避免再次访问已释放的对象指针

 
#include <cstdio>  //可以调用C中的printf 
#include <iostream>  
using namespace std;
    
class A
{
    public:
    void print(){

    }
};
    
int main()
{
    A a = A();
    a.print();

    A* b = new A();
    (*b).print();
    b->print();

    A& c = *b;//别名 
    c.print();

    printf("a value=%p\n",&a); //a value=0x7ffe4810fab7
    printf("b addr=%p\n",&b);  //b addr=0x7ffe4810fab8
    printf("b value=%p\n",b);  //b value=0x55f6813852b0
    printf("c void*=%p\n",&c); //c void*=0x55f6813852b0
    
    delete b;

    return 0;
}
        

 
变量的角度:
a,b都是一个变量,
c是一个引用,实为void*,类似于多态,本身无类型,关键看指向/引用 的是何类型

指针/地址的角度:
a是普通变量,分配在栈上,
&a就是变量a在栈上的地址,a的值指对象本身
当a是普通变量时,a直接指代值本身,即a可指代对象
一如int a= 3;一样,a可指代3这个数 

b是指针变量,分配在栈上,
&b就是变量b在栈上的地址,b的值指对象地址,非本身,
(*b)才是对象本身

A&表示这是A类型对象的一个引用/别名,是引用类型,一个类型  
因此c是对象本身,&c则是对象地址 

 
这里本人还有一个小小的疑问,
变量分配在栈上,栈在虚拟内存的高位,
new分配的地址在堆上,堆在虚拟内存的低位,
这没问题

有问题的是,栈是自高而低使用内存的,
但这里栈内存的分配却出现了自低而高的现象,
本人首次遇到这种现象,特此记录一下 

C++构造函数

类与对象

 
《C++ Primer》
C++通过类创建对象,对象中占空间的主要就是变量,比如int,float,double,char,char*等 
还有计算这些变量的方法

构造函数

 
构造函数就是个函数,它唯一特殊的地方在于它执行的时机:
在类创建对象时自动执行

利用这一点,通常做一些变量初始化工作,
当然了,你想写点逻辑处理也没啥语法上问题... 

 
#include <cstdio>  //可以调用C中的printf 
#include <iostream> 
using namespace std;

class A{
    private:
        int aa,bb;

    public:
        A(int a,int b);
        int add(){
            return aa+bb;
        }
};

A::A(int a,int b):aa(a),bb(b){}


int main()
{
    A* a = new A(1,2);
    printf("%d\n",a->add());
    return 0;
}

简化构造函数的赋值操作

 
A::A(int a,int b):aa(a),bb(b){}

等价于 
A::A(int a,int b){
	this->aa = a;
	this->bb = b;
}

C++析构函数

析构函数执行的时机:对象生命周期结束时

 
析构函数是在对象消亡时,自动被调用,用来释放对象占用的空间。

有四种方式会调用析构函数:
    1.生命周期:对象生命周期结束,会调用析构函数

    2.delete:调用delete,会删除指针类对象

    3.包含关系:对象Dog是对象Person的成员,Person的析构函数被调用时,对象Dog的析构函数也被调用。

    4.继承关系:当Person是Student的父类,调用Student的析构函数,会调用Person的析构函数
    

 
析构函数自动被调用 
如果对象中使用new分配了空间,则需要显式在析构函数中使用delete删除

引用的析构函数调用

 
#include <cstdio>  //可以调用C中的printf 
#include <iostream> 
using namespace std;
    
class A
{
private:
    int *a;
public :
    A(){
        a = new int; 
        *a = 5;
    }
    ~A()
    {
        delete a;
    }
    int get()
    {
        return *a;
    }
};
    
int main()
{
    A* b = new A();
    A& b1 = *b;
    A& b2 = *b;
    printf("b1 addr=%p\n",&b1);
    printf("b2 addr=%p\n",&b2);
    
    // delete &b1;  //这一步执行,后面会直接卡死 
    
    cout << (*b).get() << endl;
    cout << b1.get() << endl;
    
    return 0;
}

参考文章
    C++11:论构对象的构建和释放 
    C++ 析构函数何时被调用?
    变量被析构函数释放的时候的注意事项