c++柔性数组、友元、类模版

目录

1、柔性数组:

2、友元函数:

3、静态成员

注意事项

[面试题:c/c++ static的作用?](#面试题:c/c++ static的作用?)

C语言:

C++:

[为什么可以创建出 objx](#为什么可以创建出 objx)

4、对象与对象之间的关系

5、类模版


1、柔性数组:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>

using namespace std;

class MyString
{
private:
	struct  StrNode
	{
		int refcount; //引用计数
		int slen;
		int capa;
		char data[0]; //数组不填或填0就成为柔性数组
	};

private:
	StrNode* pstr;
	static const size_t DAFAUSIZE = 32;
	static const size_t INCSIZE = 2;

	static StrNode* GetMem(rsize_t n)
	{
		size_t total = (n > DAFAUSIZE) ? n + 1 : DAFAUSIZE;
		StrNode* s = (StrNode*)malloc(sizeof(StrNode) + sizeof(char) * total);
		memset(s, 0, sizeof(StrNode) + sizeof(char) * total);
		s->capa = total - 1;
		return s;
	}

	static void  FreeMem(StrNode *p)
	{
		free(p);
	}
public:
	MyString(const char* p = nullptr) :pstr(nullptr)
	{
		if (p != nullptr)
		{
			int len = strlen(p);
			pstr = GetMem(len);
			pstr->refcount = 1;
			pstr->slen = len;
			strcpy(pstr->data, p);
		}
	}
	MyString(const MyString &st) :pstr(st.pstr)  //st给当前对象
	{
		if (pstr != nullptr)
		{
			pstr->refcount += 1; //+1 说明有两个对象持有这块空间
		}
	}
	MyString(MyString &&st) :pstr(st.pstr)  //移动构造
	{
		st.pstr = nullptr;
	}

	
	~MyString() {
		if (pstr != nullptr && --pstr->refcount == 0)
		{
			FreeMem(pstr);
		}

		pstr = nullptr;
	}

	ostream& operator<<(ostream& out)const
	{
		if (pstr != nullptr)
		{
			out << pstr->data;
		}
		return out;
	}
};
ostream &operator<<(ostream &out, const MyString &st)
{
	return st << out;
}

int main()
{
	MyString s1 = "chengxi";
	MyString s2(s1);
	MyString s3(s2);
	MyString s4(s3);
	cout << s3 << endl;
	return 0;
}

2、友元函数:

(1)不具有传递特性、继承特性、是单向的。

(2)友元函数分为:函数友元、成员函数友元、类友元

(3)友元函数是访问类的对象非公有属性。

成员函数友元:

cpp 复制代码
using namespace std;
class Base;
class Test
{
private:int sum;
public:
	Test(int x = 0) :sum(x) {}
	void SetSum(int x = 0)
	{
		sum = x;
	}
	int GetSum()const { return sum; }
	int Add(const Base &it);
};


class Base
{
	friend int Test::Add(const Base& it);//成员函数友元
	int num;
public:
	Base(int x = 0) :num(x)
	{

	}
};
int Test::Add(const Base& it)
{
	return this->sum + it.num;
}

int main()
{
	Test t1(100);
	Base b1(2);
	t1.Add(b1);
}

类友元:

cpp 复制代码
using namespace std;
//Test对象访问base的私有和公有,注意方向
class Base
{
	friend class Test; 
private:
	int num;
public:
	Base(int x = 0) :num(x)
	{
	}
};

class Test
{
private:int sum;
public:
	Test(int x = 0) :sum(x) {}
	void SetSum(int x = 0){	sum = x;}
	int GetSum()const { return sum; }
	int Add(const Base &it) {
		return it.num + 10;
	}
	int func(const Base& it)
	{
		return it.num;
	}
};

int main()
{
	Test t1;
	Base b1;
	t1.Add(b1);
	t1.func(b1);
	return 0;
}

3、静态成员

(1) 类静态成员只能在类外进行初始化,

(2)静态成员变量在数据区存储。静态量不属于对象,是被所有对象共享,不管有多少个对象,静态成员只有一份存在于数据区。

(3)静态数据可以在普通方法中使用

(4)为什么不能在参数列表中对静态成员初始化?

答:参数列表,在定义对象的时候要调用构造函数,拿参数列表对数据成员进行创建,静态成员被所有对象共享,在定义不同的对象时都要对静态成员进行构建,c++中,数据成员(对象、变量)在生存期内只能被构建一次。放在参数列表中,被构建了n次,不允许。要在类外进行初始化。

静态成员在类模版中,数据类型不同 ,就会产生不同的 "静态成员"

cpp 复制代码
using namespace std;

template<class T>
class Object
{
private:
	int value;
protected:
	static int num;
public:
	Object(int x=0):value(x){}
	~Object(){}
};

class Test :public Object<Test>
{
public:
	Test()
	{
		cout << "Create Test::" << (num += 1) << endl;
	}
};

class Base :public Object<Base>
{
public:
	Base() {
		cout << "Create Base:" << (num += 1) << endl;
	}
};
template<class T>
int Object<T>::num = 0;
int main() {
	Test t1[1];
	Base b1[2];
	return 0;
}
  • Test t1[1];:创建一个包含 1 个 Test 对象的数组,在创建 Test 对象时,会调用 Test 类的构造函数,输出相应的信息。
  • Base b1[2];:创建一个包含 2 个 Base 对象的数组,在创建 Base 对象时,会调用 Base 类的构造函数,输出相应的信息。

注意事项

由于 Object 类的静态成员变量 num 是模板化的,Object<Test>Object<Base> 是不同的模板实例化,它们各自拥有独立的 num 副本。所以在创建 TestBase 对象时,它们的 num 计数是相互独立的。

面试题

C语言:

(1)静态关键字修饰全局/局部变量,存在于数据区,当函数消亡时,静态变量还存在,可以&。

全局变量有static和无的区别?

(2)全局静态变量只在本文件中可见,同一工程的其他文件不可见, (私有),未用static修饰的全局变量,可以用extern在其他文件用。

(3)static修饰函数,此函数只在当前文件夹有效,在其他文件中不能用,(私有)。修饰函数本身而不是修饰返回值 static int* add() {} ,。

C++:
(1)static可以修饰类的成员,要在类外进行初始化。所有的对象都共享同一个静态成员变量,派生的类也共享(但前提是将其设置成protected /public)。

(2)静态成员变量不属于对象,无this,在常方法中可以修改静态成员变量的值,this不修饰它。

static可以修饰属性的类型,要在类外进行初始化,如果是静态常性变量可以在类内进行初始化,但必须要求是整型类型。

(3)static修饰成员方法,static void func(){} 无this指针。静态成员方法无this。

(3)static修饰的成员方法可以访问静态属性也可以访问非静态属性(非静态属性必须把对象传进来)。

静态成员方法可以通过 对象访问,也可以通过类名。

静态成员变量
  • 类的所有对象共享:静态成员变量属于类本身,而不是类的某个对象。无论创建多少个类的对象,静态成员变量都只有一份副本,被所有对象共享。
  • 需要在类外初始化:静态成员变量必须在类外进行初始化,因为它不依赖于对象的创建。

:obja可以创建出来,指针:4字节

cpp 复制代码
#include<stdio.h>
#include<iostream>
using namespace std;
class Object
{
private:
	int value;
private:
	static Object objx;
private:
	Object(int x = 0) :value(x) {cout << "create Obj" << this << endl;}
	~Object(){cout << "destroy obj:" << this << endl;}
};
Object Object::objx(1);

int main()
{
	return 0;
}
  • 成员变量

    • int value;:一个私有成员变量,用于存储对象的值。
    • static Object objx;:一个私有静态成员变量,类型为 Object 类本身。静态成员变量属于类,而不是类的某个对象,所有该类的对象共享同一个静态成员变量。
  • 静态成员变量初始化
    *

    复制代码
      Object Object::objx(1);

    这行代码对 Object 类的静态成员变量 objx 进行初始化,调用了 Object 类的构造函数,并将参数 1 传递给构造函数,将 value 成员变量初始化为 1

    为什么可以创建出 objx

    虽然 Object 类的构造函数和析构函数都是私有的,通常情况下,外部代码无法直接创建 Object 类的对象。但是,静态成员变量 objx 是类的一部分,类的内部可以访问其私有成员。因此,在类的外部对静态成员变量 objx 进行初始化时,实际上是在类的作用域内调用了构造函数,这是被允许的。

    当程序开始执行时,在进入 main 函数之前,会先对静态成员变量进行初始化,所以 Object::objx 会被创建,调用构造函数输出创建信息。当程序结束时,静态对象 objx 会被销毁,调用析构函数输出销毁信息。

cpp 复制代码
using namespace std;
class Object
{
private:
	int value;
private:
	static Object objx;
private:
	Object(int x = 0) :value(x) {cout << "create Obj" << this << endl;}
	~Object(){cout << "destroy obj:" << this << endl;}
public:
	void Print()const { cout << "value" << value << endl; }
	void SetValue(int x) { value = x; }
	int GetValue()const { return value; }
	//通过静态方法将静态对象的地址返回
	static Object* getObject()
	{
		return &objx;
	}
};
Object Object::objx(1);

int main()
{
	Object* pa = Object::getObject();
	Object* pb = Object::getObject();
	if (pa == pb) { cout << "pa,pb pinter:object" << endl; }
	pa->Print();
	pa->SetValue(100);
	pa->GetValue();
	pa->Print();
	return 0;
}
  • if (pa == pb) { cout << "pa,pb pinter:object" << endl; }:比较 papb 的值,如果相等,说明它们指向同一个对象,输出相应信息。由于 objx 是静态对象,无论调用多少次 getObject() 函数,返回的都是同一个对象的地址,所以 papb 相等。
  • 通过静态成员函数可以在类外部访问私有静态成员对象,并且由于静态成员对象只有一个实例,无论通过多少个指针访问,操作的都是同一个对象。

4、对象与对象之间的关系

依赖关系:用-------> 表示

一个对象在运行期间会用到另一个对象的关系,通常在运行期间产生,并且伴随运行时的变化依赖关系也发生变化。主要表现为 :"使用关系"。

关联关系:用------> 表示:

静态关系,如:学生和学校,是关联关系

在设计中。关联关系主要体现在目标类型的指针或引用。

聚合、继承、组合等。

5、类模版

template<类型模版参数表>

类模板不是类,因此无法直接实例化对象,需要在声明类时给定确定的数据类型,编译器才会以此分配内存。对于普通类而言,类的声明和类成员函数的定义可以写在不同的文件中,但是对于类模板来说,最好定义在同一个文件中。

复制代码
//define _N 10
template<class T,int N>
class SeqStack
{
	T* data;
	int capa;
	int top;
	static const int stackinitsize = 100;
	static const int incsize = 2;
	static T* GetMen(size_t sz)
	{
		T* newdata = (T*)malloc(sizeof(T) * sz);
		if (nullptr == newdata)
		{
			cout << "malloc fail" << endl;
			exit(1);
		}
		memset(newdata, 0, sizeof(T) * sz);
		return newdata;
	}
	static void FreeMem(T* p)
	{
		free(p);

	}

public:
	SeqStack(int sz = stackinitsize) :data(nullptr), capa(sz), top(-1)
	{
		//data = (T*)malloc(sizeof(T) * capa); //只申请空间不构建对象
		data = GetMen(capa);
	}
	~SeqStack()
	{

	}
	int size()const { return top + 1; }
	bool empty()const { return size() == 0; }
	int capacity()const { return capa; }
	bool full()const { return size() == capacity(); }
	bool push(const T& x)
	{
		if (full())return false; //满了调增容函数 
		top += 1;
		new(&data[top]) T(x);   //data[++top] = x;构建对象
		return true;
	}
	bool gettop(T *val) {
		if (empty())return false;
		val = data[top];
		return true;
	}
	bool poptop(T& val)
	{
		if (empty())return false;
		val = data[top];
		(&data[top])->~T(); //析构top中的对象
		top -= 1;
		return true;
	}
	bool pop()
	{
		if (empty())return false;
		(&data[top])->T();
		top -= -1;
		return true;
	}

};
int main()
{
	SeqStack<int, 10> istack;   //using T:int N:10

}

int main() {
    ClassName<int> intObj(10);  // 实例化类模板,创建一个处理 int 类型的类对象
    int result = intObj.getData();
    std::cout << "Result: " << result << std::endl;

    ClassName<double> doubleObj(3.14);  // 实例化类模板,创建一个处理 double 类型的类对象
    double dResult = doubleObj.getData();
    std::cout << "Result: " << dResult << std::endl;

    return 0;
}
相关推荐
我不会编程55514 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python
李少兄14 小时前
Unirest:优雅的Java HTTP客户端库
java·开发语言·http
懒羊羊大王&14 小时前
模版进阶(沉淀中)
c++
无名之逆14 小时前
Rust 开发提效神器:lombok-macros 宏库
服务器·开发语言·前端·数据库·后端·python·rust
似水এ᭄往昔14 小时前
【C语言】文件操作
c语言·开发语言
啊喜拔牙14 小时前
1. hadoop 集群的常用命令
java·大数据·开发语言·python·scala
owde15 小时前
顺序容器 -list双向链表
数据结构·c++·链表·list
xixixin_15 小时前
为什么 js 对象中引用本地图片需要写 require 或 import
开发语言·前端·javascript
GalaxyPokemon15 小时前
Muduo网络库实现 [九] - EventLoopThread模块
linux·服务器·c++
W_chuanqi15 小时前
安装 Microsoft Visual C++ Build Tools
开发语言·c++·microsoft