Funkcja sklejająca łanuchy

0

Napisałem taki kod:

#include <iostream>
#include <string.h>

	class String {
	public:
		String(char* s = "") {
			len = strlen(s);
			_s = new char[len + 1];
			strcpy(_s, s);
		}

		String(String& obj) {
			len = strlen(obj._s);
			_s = new char[len + 1];
			strcpy(_s, obj._s);
		}
		void operator=(String& obj) {
			len = strlen(obj._s);
			_s = new char[len + 1];
			strcpy(_s, obj._s);
		}
		String& operator+(String& obj) {
			char* p = new char[strlen(_s) + 1 + strlen(obj._s) + 1];

			int len1 = strlen(_s);
			int len2 = strlen(obj._s);
			int i = 0;
			while(i < len1) {
				*p++ = *_s++;
				i++;
			}
			i = 0;
			while(i < len2) {
				*(p+i) = *obj._s;
				i++;
			}
			String n(p);

			return n;
		}
		void print(char c) {
			std::cout<<c <<_s;
		}
		void print() {
			std::cout<<_s;
		}
		
	private:
		char* _s;
		int len;
	};


int _tmain(int argc, _TCHAR* argv[])
{
	String s("Ala");
	s.print('\n');
	String g(s);
	g.print('\n');
	String z("MVVV");
	g = z;
	g.print();

	String m = s + g;
	m.print('\n');

	std::cin.get();
        std::cin.get();


	return 0;
}

Nie mogę sobie jednak poradzić z operatorem+. Jak go zrealizować? Moje próby powodują wycieki pamięci. Dodam tylko, że chętnie bym skorzystał z klasy std::string ale wykładowca życzy sobie korzystania z char*.
Podrzuccie mi jakiś pomysł, wskazówkę, pseudokod :).

0
while(i < len1) {
   *p++ = *_s++;
    i++;
}
i = 0;
while(i < len2) {
  *(p+i) = *obj._s;
  i++;
}
*(p+i)

tak

*p++

nie
przesuwasz wskaźnik a później go nie cofasz przez co masz wycieki. Wystarczy że w pierwszym while zrobisz dokładnie to samo co w drugim while i powinno pomóc
Dodatkowo w drugim przepisujesz ciągle ten sam znak

while(i < len1) {
  *(p + i) = *(_s + i);
  i++;
}
while(i < len2) {
  *(p + i) = *(obj._s + i);
  i++;
}
0
  1. Dopisz metodę length() która zwraca długość napisu, nie korzystaj bezpośrednio w kodzie ze strlen(cośtam) bo się bardzo ograniczasz
  2. Pola maja być prywatne, widziałeś żeby z std::string dało się tak na pałę wyciągać char*?
 char* p = new char[strlen(_s) + 1 + strlen(obj._s) + 1];

za dużo o 1
4. Za wielokrotne wołanie strlen() powinni łamać kołem bo ta funkcja działa w O(n)
5. Druga pętla kopiująca jest błędna bo nie przesuwasz wskaźnika źródłowego a docelowy jest przesuwany źle.
6. Nie dopisujesz na końcu stringa znaku '\0' a powinieneś bo pętle go nie kopiują

0
String operator+( String& obj )
{
	int len1 = strlen( _s );
	int len2 = strlen( obj._s );
					
	char* p = new char[ len1 + len2 + 1];
/*
	int i = 0;
	while( p[ i ] = _s[ i++ ] ); // jeden sposób
	i = 0;
              while( *( p + len1 + i ) = *( obj._s + i++ ) ); // drugi sposób
*/ // lub lepiej
	strcpy( p, _s );
	strcpy( p + len1 , obj._s );

	String s( p );
						
	return s;
}

Zwróć kopię Stringa, zwrócenie referencji wywołuje access violation. Użyj metod strcpy. A wycieki pamięci masz, ponieważ nie posiadasz destruktorów które zwalniałyby pamięć _s.

0

Kolejny błąd który wcześniej przeoczyłem: zwracasz referencje do LOKALNEGO obiektu...

0

Dziękuję za pomoc i krytykę.
Teraz lepiej to wygląda? :-)

#include <iostream>
#include <string.h>
 
class String {
public:
	String(char* s = "") {
		_s = new char[len(s) + 1];
		strcpy(_s, s);
	}
	String(String& obj) {
		_s = new char[obj.len() + 1];
		strcpy(_s, obj.getText());
	}
	~String() {
		delete[] _s;
	}
	void operator=(String& obj) {

		_s = new char[obj.len() + 1];
		strcpy(_s, obj.getText());
	}
	String operator+(String& obj) {
		char* p = new char[len() + obj.len() + 1];

		strcpy(p, _s);
		strcpy(p + len(), obj.getText());
		String n(p);

		return n;
	}
	void print(char c) {
		std::cout<<c <<getText();
	}
	void print() {
		std::cout<<getText();
	}
	int len() {
		return strlen(_s);
	}
	int len(char* s) {
		return strlen(s);
	}
	char* getText() {
		return _s;
	}

private:
	char* _s;

};

 
int _tmain(int argc, _TCHAR* argv[])
{
	String x("ABCDE");
	String y("12345");

	String z = x + y;
	z.print();

    std::cin.get();
	std::cin.get();
 
    return 0;
}

Shalom, czy nawet wewnątrz klasy nie mogę korzystać ze zmiennych prywatnych tylko muszę za pomocą akcesorów się nimi zajmować?

0
int len(char* s) {
                return strlen(s);
        }

Metody tego typu są bez sensu. Tym bardziej niestatyczne.

W ogóle leży u ciebie const-correctness. W operatorach + i = masz wyciek pamięci (stosuj Visual Leak Detector). Zastanów się nad zamianą metody print na taką, która przyjmuje const std::ostream i przeciążony operator << do tego. W C++ stosuj nagłówek cstring, a nie string.h. Nie zatrzymuj sztucznie programu konsolowego. Postaw breakpoint jak chcesz zanalizować wartość zmiennych i użyj do tego debuggera albo uruchamiaj program przez CTRL-F5.

1

Napisałem podobną klasę.

Użycie:

String test1 = "Hello";
String test2 = getSpaceOrNewline(true);
test1 += test2;

String test3 = test1 + "world!";

String test4;
test4 = getSpaceOrNewline(false);
	
test2 = test4;

std::cout << test3.getCString() << test2.getCString();

Klasa nie jest skomplikowana, ba, jest bardzo prosta z perspektywy C++. Ma to, co każda klasa, która sama zarządza pamięcią powinna mieć, czyli co najmniej trzy konstruktory oraz dwa operatory przypisania i każdy z nich jest użyty w kodzie powyżej ;).
move constructor i move assignment operator można wywalić, jeżeli nie chcemy korzystać z C++11, kod też będzie działać.

Wynik (debug):

Visual Leak Detector Version 2.2.3 installed.
conversion construtor
conversion construtor
move construtor
conversion construtor
copy construtor
move construtor
default construtor
conversion construtor
move construtor
move assignment operator
copy construtor
copy assignment operator
Hello world!
No memory leaks detected.
Visual Leak Detector is now exiting.

Cały kod:

#include <vld.h>

#include <cstring>
#include <iostream>

// NULL-terminated C-style string
class String
{
private:
	char* _chars;
	size_t _length;

public:
	~String()
	{
		delete _chars;
	}

	// default constructor
	String() : _chars(NULL), _length(0)
	{
		std::cout << "default construtor\n";
	}

	// conversion constructor
	String(const char* cstring)
	{
		_length = strlen(cstring);
		_chars = new char[_length + 1];
		strcpy(_chars, cstring);

		std::cout << "conversion construtor\n";
	}

	// copy construtor
	String(const String& other)
	{
		_length = other.getLength();
		_chars = new char[_length + 1];
		strcpy(_chars, other.getCString());

		std::cout << "copy construtor\n";
	}

	// move constructor
	String(String&& other) : _chars(NULL), _length(0)
	{
		swap(*this, other);

		std::cout << "move construtor\n";
	}

	// copy assignment operator
	String& operator=(const String& other)
	{
		String copy(other);
		swap(*this, copy);

		std::cout << "copy assignment operator\n";

		return *this;
	}

	// move assignment operator
	String& operator=(String&& other)
	{
		swap(*this, other);

		std::cout << "move assignment operator\n";

		return *this;
	}

	friend void swap(String& first, String& second)
	{
		std::swap(first._length, second._length);
		std::swap(first._chars, second._chars);
	}

	const char* getCString() const
	{
		return _chars;
	}

	size_t getLength() const
	{
		return _length;
	}

	String& operator+=(const String& another)
	{
		_length += another.getLength();
		char* buffer = new char[_length + 1];
		strcpy(buffer, _chars);
		strcat(buffer, another.getCString());

		delete _chars;
		_chars = buffer;

		return *this;
	}

	String operator+(const String& another)
	{
		String result(*this);
		result += another;

		return result;
	}
};

String getSpaceOrNewline(bool choice)
{
	String ret = choice ? " " : "\n"; return ret;
}

int main()
{
	String test1 = "Hello";
	String test2 = getSpaceOrNewline(true);
	test1 += test2;

	String test3 = test1 + "world!";

	String test4;
	test4 = getSpaceOrNewline(false);
	
	test2 = test4;

	std::cout << test3.getCString() << test2.getCString();

	return 0;
}
0

Jeszcze @Rev ewentualnie dołożyłbym:

operator char*( )
{
	return _chars;
}

I wtedy zamiast:

std::cout << test3.getCString( );

Używałoby się go

std::cout << test3;

1 użytkowników online, w tym zalogowanych: 0, gości: 1