c++基础知识(3)析构函数.static.const.友元函数

本文详细介绍了C++中的析构函数,包括何时自动调用、如何处理资源释放以及注意事项。同时,探讨了静态成员的用法,包括全局静态变量、局部静态变量以及静态成员函数的特性。此外,还阐述了const在类成员变量和成员函数中的应用,强调其在保护数据不变性方面的重要性。最后,提到了友元函数的概念及其在访问类私有成员时的角色。

一.析构函数:对象销毁自动调用的函数,它可以显示使用

~类名(void)

注意:

1.当存在移动构造时,需要在释放空间之前判断是否是临时变量,如果是临时变量则不需要释放

2.使用析构显示调用时需要注意重复释放的问题

#include <iostream>
#include <cstring>
using namespace std;

class String{
	
	public:
		//普通构造
		String(const char*p)
		{
			/*
			if(nullptr == p)
				异常处理
			*/
			cout<<"normal"<<endl;
			addr = new char[strlen(p)+1];  //开辟空间
			strcpy(addr, p);
			len = strlen(p);
		}
		//深拷贝
		String(const String & obj)
		{
			cout<<"cpy"<<endl;
			addr = new char[strlen(obj.addr)+1];
			strcpy(addr,obj.addr);
			len = obj.len;
		}
		//移动构造
		String(String && obj)
		{
			cout<<"move"<<endl;
			addr = obj.addr;
			obj.addr = nullptr;
			len = obj.len;
		}

        ~String()
        {
            cout<<"delte"<<endl;
            if(addr != nullptr)
            {
                delete []addr;
                addr = nullptr;
            }
        }
		char &at(const int id)   //因为需要通过返回值修改成员变量所以使用引用
		{
			return addr[id];			
		}
		
		char* get_addr()
		{
			return addr;
		}
		
		void setval(const char* p)
		{
			
			if(strlen(p) > len)
			{
				//p大于原来的空间,应该重新开辟一个空间
				delete []addr;
				addr = new char[strlen(p)+1];
				
				strcpy(addr, p);
				len =  strlen(p);
			}
			else
			{
				strcpy(addr, p);
				len =  strlen(p);
			}
		}
		
		void append(const char* p)
		{
			char buf[len+1];    //存储原始数据
			strcpy(buf, addr);
			delete []addr;
			addr = new char[len+strlen(p)+1];
			
			strcpy(addr, buf);
			strcat(addr, p);
			len += strlen(p); 
		}
	private:
		char *addr;
		int len;
};

void test()
{
    char buf[100] = "hello";
    String A = buf;

}

int main()
{
    test();
    while(1);
}

二.static

1.修饰变量

{

        1.修饰全局变量,生命周期不变,作用域被限制在本文件

        2.修饰局部变量,延长生命周期到程序结束,作用域不变

}

2.修饰函数

{

        1.修饰函数,生命周期不变,作用域限制在本文件

}

3.修饰类相关

[

        特点:静态成员,不和具体对象关联,没有对象生成静态成员也存在                        

                   静态成员不属于类,多个对象公用一个静态成员

                   静态成员使用之前必须初始化,类型+类名::变量名=n;

                   联系:利用static变量,计算某个类对象个数

修饰共有成员

#include <iostream>

using namespace std;

class demo{

    public:
        void setval(int n)
        {
            demo::a = n;
        }
        
        int getval()
        {
            return demo::a;
        }
		
        static int a;
        //int b;
};

int demo::a = 1; //static init

int main()
{
   // demo  b;

    demo::a=100;

    cout<<demo::a<<endl;

}

修饰私有成员

#include <iostream>

using namespace std;

class demo{

    public:
        void setval(int n)
        {
            demo::a = n;
        }
        
        int getval()
        {
            return demo::a;
        }
	private:
		static int a;
        //int b;
};

int demo::a = 1; //static init

int main()
{
    demo  b, c;

   //因为静态变量只有一份,所以b改成10, c打印依然是10 
    cout<<b.getval()<<endl;
    cout<<c.getval()<<endl;
    b.setval(10); 
    cout<<b.getval()<<endl;
    cout<<c.getval()<<endl;

	cout<<sizeof(demo)<<endl;//打印出来是1,说明静态成员不属于类
}

1.修饰成员变量的情况

2.修饰成员函数

{

        (1).不能访问非静态的类成员

        (2).非静态的类成员可以访问静态成员

        (3).静态成员没有this指针

#include <iostream>

using namespace std;

class demo{
    public:
        demo()
        {
            a++;
        }
        demo(demo&obj)
        {
            a++;
        }
        demo(demo &&obj)
        {
            a++;
        }

        static int getval()
        {
            return a;
        }
        
        void func()
        {
            getval();
        }

        ~demo()
        {
            a--;
        }
    private:
        static int a;
        int b;
};

int demo::a = 0;

void func()
{
    //demo A,B,C;
    cout<<demo::getval()<<endl;
}

int main(int argc, char *argv[])
{ 

    //demo A, B, C;

    func();
    cout<<demo::getval()<<endl;

    return 0;
} 

              关于this指针

                {

                        thie指针       

#include <iostream>

class demo{
    
    public:
        //形参和成员变量同名
        void setval(int a)
        {
            this->a=a;
        }
        
        //返回对象指针
        demo *func()
        {
            return this;
        }
        //返回对象
        demo &func()
        {
            return *this;
        }

    private:
        int a;

}

int main()
{
    demo A;
}

               

                }

}

]

三.const

1.const修饰类的成员变量:

(1).修饰的变量不能被更改,const修饰的变量需要初始化

(2).const修饰的成员变量,必须使用初始化列表进行初始化

(3).非const成员函数可以访问const修饰的变量

初始化列表

{

        1.可以初始化const修饰的变量,也可以初始化非const修饰的变量

}

#include <iostream>
using namespace std;
class demo{
   // 非const成员函数可以访问,const成员变量
    public:
        demo(int n, char c , int *p):
            a(n),ch(c),b{p[0], p[1], p[2]} //初始化列表格式,分别对int,char,数组,进行的初始化
        {
        
        }
        
        void printf_demo()
        {
            cout<<a<<ch<<endl;
            cout<<b[0]<<b[1]<<b[2]<<endl;
        }
    private:
        const int a;
        const char ch;
        int b[3];
};


int main()
{
    int buf[3] = {1,2,3};
    demo A(10, 'A',buf);

    A.printf_demo();


}

2.const修饰函数

格式:  返回值 函数名()const

    1.const修饰的函数可以访问const成员

    2.const修饰的函数可以访问非const成员

重点3.函数内部不能出现修改变成员量值的逻辑出现!

可以看出其实const的作用并不在于访问,而是在与改不改变

#include <iostream>
using namespace std;
class demo{
   // 非const成员函数可以访问,const成员变量
    public:
        demo(int n, char c , int *p):
            a(n),ch(c),b{p[0], p[1], p[2]} //初始化列表格式,分别对int,char,数组,进行的初始化
        {
        
        }
        
        void printf_demo()
        {
            cout<<a<<ch<<endl;
            cout<<b[0]<<b[1]<<b[2]<<endl;
        }

        int &func()const{
            cout<<b[0]<<endl;  //1.const修饰的函数可以访问非const成员
            cout<<a<<endl;    //2.const修饰的函数可以访问const成员
            
            //return b[0];      // err,因为引用有可能改变数据,所以就是没改也过不了
        }
    private:
        const int a;
        const char ch;
        int b[3];
};


int main()
{
    int buf[3] = {1,2,3};
    demo A(10, 'A',buf);

    A.func();

}

3.const修饰类

   1.const修饰的对象,只能访问有const修饰的函数

四.友元函数

面向对象的特点

1.封装:安全

2.继承

3.多态

友元函数

#include <iostream>

using namespace std;

class demo{


    private:
        friend void func(demo A);  //友元函数声明
        int a;
};

void func(demo A)
{
    cout<<A.a<<endl;
}

int main()
{
    demo A;
    func(A);   
}

作用:友元函数可以访问类的私有空间

一.全局函数作为友元函数

二.友元类  向前声明、

#include <iostream>

using namespace std;

class B; //向前声明

class A{
    
    private:
        friend class B;
        int i;
};

class B{

    public:
        void func(A obj)
        {
            cout<<obj.i<<endl;
        }


    private:
        int j;
};

int main()
{
    B i;
    A j;

    i.func(j);
}

三.类的成员函数是另一个类的友元函数

#include <iostream>

using namespace std;

class A;

class B{
    
    public:
        void func(A obj);

    private:

};

class A{

    public:
        friend void B::func(A obj);

    private:
        int i;
};

void B::func(A obj)
{
    cout<<obj.i<<endl;
}
int main()
{
    A i;
    B j;

    j.func(i);
}

1.友元函数内部没有this指针

2.打破了封装,不安全(避免使用)

3.需要频繁访问数据,使用友元会更快捷

关键字 friend

<think>好的,用户现在想了解C++中虚函数的特性,特别是关于inline、造函数、析构函数static函数能否作为虚函数。我之前已经回答过菱形继承的问题,用户可能是在学习C++的多态性,现在想深入理解虚函数的使用限制。 首先,我需要回顾用户提供的引用内容。引用[1]、[2]、[3]、[4]和[5]都提到了虚函数与其他函数修饰符的关系。比如,造函数、static函数、内联函数和友元函数不能是虚函数。用户的问题明确提到了这几个关键字,所以需要逐一解释。 用户可能已经知道虚函数的基本概念,但需要更详细的特性说明。我需要分点回答每个函数是否能成为虚函数,并解释原因。例如,造函数不能是虚函数,因为对象在造阶段还没有虚表,无法动态绑定。这需要引用[4]提到的内容,造函数的作用是初始化对象,虚函数的调用依赖于虚表,而虚表在对象造完成后才存在,因此矛盾。 接下来是inline函数。根据引用[1]和[3],inline在编译期展开,而虚函数在运行期通过虚表动态决定,所以虚函数不能是inline。不过,可能存在特殊情况,比如编译器是否会对虚函数进行内联优化,但根据标准,虚函数不能声明为inline,这一点需要明确。 然后是static函数。引用[3]指出static函数属于而非对象,不能被继承,而虚函数依赖于对象的虚表,所以static函数不能是虚函数。需要解释static函数和虚函数的作用域不同,导致无法共存。 析构函数的情况不同,用户可能知道虚析构函数的重要性,特别是在多态基中。需要强调虽然析构函数可以是虚函数,但造函数不能。引用[2]提到虚析构函数的使用,确保正确释放子资源,这需要详细说明。 用户可能还希望了解每个限制背后的原理,而不仅仅是规则。例如,为什么造函数不能是虚函数,需要联系对象创建的过程和虚表建立的时机。同时,可能需要提到友元函数,虽然用户没问,但引用里提到友元函数不能被继承,所以也不能是虚函数,是否需要补充? 另外,用户引用的内容中有关于const、enum、static和inline的其他信息,但当前问题集中在虚函数相关的特性,所以需要过滤掉不相关的部分,专注于用户的问题点。 最后,确保回答结清晰,每个函数型作为一个小节,解释能否作为虚函数,原因,并引用相关文献。同时,用户之前的问题涉及菱形继承的解决方案,可能对多态性有基础,因此可以适当链接到之前的回答,但当前问题更关注虚函数本身的限制。 需要检查是否有遗漏点,比如友元函数是否在问题范围内,但用户没有明确提到,可能不需要展开。总结各个点,确保覆盖inline、造函数、析构函数static函数,每个部分都给出明确的结论和原理,并引用提供的资料来支持。</think>#### C++中虚函数的关键特性与限制 以下是针对虚函数与`inline`、造函数、析构函数、`static`函数之间关系的详细分: --- ### 一、虚函数与`inline`函数的关系 1. **虚函数不能是`inline`函数** - **编译期与运行期机制冲突** `inline`函数在编译期展开(代码替换),而虚函数需通过虚表(`vtable`)在运行期动态绑定具体实现。两者机制本质冲突,因此虚函数无法内联[^1][^3]。 - **例外情况** 若虚函数通过对象(而非指针/引用)调用,编译器可能在编译期静态绑定并内联优化,但此时已失去多态性。 --- ### 二、造函数能否为虚函数 1. **造函数不能为虚函数** - **对象造顺序限制** 造函数的作用是初始化对象的内存布局(包括虚表指针)。虚表在造函数执行完成后才建立,因此虚函数调用所需的动态绑定在造阶段无法实现[^4]。 - **逻辑矛盾** 若造函数为虚函数,需通过虚表调用,但虚表此时尚未初始化,导致“先有鸡还是先有蛋”的问题。 --- ### 三、析构函数与虚函数 1. **析构函数应为虚函数(多态基)** - **避免资源泄漏** 若基指针指向派生对象,且基析构函数非虚,则`delete`基指针时仅调用基析构函数,导致派生资源未释放[^2]。 - **示例** ```cpp class Base { public: virtual ~Base() {} // 虚析构函数 }; class Derived : public Base { ~Derived() { /* 释放派生资源 */ } }; Base* obj = new Derived(); delete obj; // 正确调用Derived::~Derived() ``` --- ### 四、`static`函数能否为虚函数 1. **`static`函数不能为虚函数** - **作用域与调用方式冲突** `static`函数属于而非对象,不依赖`this`指针,而虚函数通过对象虚表动态绑定,需`this`指针访问虚表。两者作用域与调用机制不兼容[^3]。 - **示例** ```cpp class MyClass { public: static virtual void func(); // 错误:static函数不能为虚函数 }; ``` --- ### 五、其他限制 1. **友元函数不能为虚函数** - 友元函数不属于的成员函数,无法被继承或覆盖。 2. **虚函数与`const`/`volatile`修饰** - 虚函数可被`const`或`volatile`修饰,但需在派生中保持一致的修饰符。 --- ### 总结表 | 函数型 | 能否为虚函数 | 原因 | |----------------|--------------|----------------------------------------------------------------------| | `inline`函数 | 否 | 编译期展开与虚函数动态绑定冲突[^3] | | 造函数 | 否 | 虚表未初始化,无法动态绑定[^4] | | 析构函数 | 是(推荐) | 确保多态对象正确释放资源[^2] | | `static`函数 | 否 | 属于而非对象,不依赖虚表[^3] | | 友元函数 | 否 | 非成员函数 | --- ### 相关问题 1. 为什么C++需要虚析构函数? 2. 虚函数表(vtable)在内存中如何存储? 3. 如何通过`final`关键字限制虚函数的重写?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞赴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值